使用goner/otel/meter实现应用指标监控

分布式系统全链路监控之一:分布式全链路监控基础概念和OpenTelemetry OpenTelemetry(OTel)是一个开源、厂商无关的可观测性框架,用于采集和导出追踪、指标和日志等遥测数据。它支持分布式系统监控,通过链路追踪(Traces)、指标(Metrics)和日志(Logs)等信号提供系统可观测性。OTel的核心概念包括上下文传播、信号关联以及结构化日志处理,帮助开发者理解系统行为并排查问题。其收集器(Collector)可统一处理多种数据源,自动关联日志与追踪信息,推荐使用结构化日志以提升分析效率。OTel已成为行业标准,被广泛集成于各类应用中。 阅读详情

引言

在现代微服务架构和分布式系统中,可观测性(Observability)已经成为确保系统稳定性和性能的关键因素。可观测性通常由三大支柱组成:日志(Logs)、指标(Metrics)和追踪(Traces)。本文将重点介绍其中的"指标"部分,探讨如何使用Gone框架的goner/otel/meter组件来高效地收集、管理和可视化应用指标数据。

通过构建一个健壮的指标监控系统,开发团队可以实现:

  • 实时监控应用性能和健康状态
  • 提前发现潜在问题,防患于未然
  • 建立基于数据的决策机制,优化系统性能
  • 有效进行容量规划和资源分配

技术栈介绍

Gone框架

Gone是一个轻量级的Go语言依赖注入框架,设计理念类似于Java的Spring框架。它通过依赖注入的方式组织代码,使应用更加模块化、可维护和可测试。Gone框架的核心优势在于其简洁而强大的组件管理机制,可以轻松地将各种功能模块集成到应用中。

OpenTelemetry (OTel)

OpenTelemetry是一个开源的可观测性框架,它提供了一套标准化的API、库和代理,用于收集和处理遥测数据(日志、指标和追踪)。OpenTelemetry的出现解决了可观测性工具碎片化的问题,使开发者可以使用统一的API收集遥测数据,然后将这些数据发送到任何后端分析工具。

goner/otel/meter是Gone框架对OpenTelemetry指标收集功能的封装,它使得在Gone应用中集成OpenTelemetry变得非常简单。

Prometheus

Prometheus是一个开源的监控和警报系统,专为高度动态的云原生环境设计。它通过以下特性脱颖而出:

  • 多维数据模型:每个时间序列由指标名称和键值对标签定义
  • 强大的查询语言PromQL:支持复杂的数据聚合和转换
  • 无需依赖存储:支持本地存储和远程存储
  • 基于HTTP的Pull模式采集:简化了网络配置
  • 支持Push模式的网关:用于短期任务的监控
  • 丰富的可视化集成:原生支持Grafana等工具

在本教程中,我们将使用Prometheus作为指标数据的存储和查询后端。

快速开始

1. 准备工作

首先,我们需要安装Gone框架的命令行工具gonectl。它能帮助我们快速创建和管理Gone项目:

go install github.com/gone-io/gonectl@latest

gonectl工具功能强大,提供了项目脚手架、代码生成和项目管理等功能,大大简化了使用Gone框架的开发流程。确保您的$GOPATH/bin已添加到系统路径中,这样才能在命令行中直接使用gonectl命令。

2. 创建示例项目

使用gonectl创建一个基于otel/meter/prometheus模板的项目。这个模板已经包含了所有必要的监控组件配置:

gonectl create -t otel/meter/prometheus meter-prometheus

执行这个命令后,gonectl会自动创建一个名为meter-prometheus的项目目录,并生成所有必要的文件和配置。这个过程中,gonectl主要完成了以下工作:

  • 创建基本的项目结构
  • 自动生成Gone框架所需的依赖注入配置文件
  • 配置OpenTelemetry和Prometheus相关组件
  • 添加示例控制器和API端点
  • 创建Docker Compose配置,便于启动Prometheus服务

3. 启动应用

进入项目目录,运行以下命令启动应用服务:

go run ./cmd

当你看到类似于Server started at :8080的输出时,表示应用已经成功启动。此时,Gone框架已经自动完成了以下工作:

  • 初始化了所有注册的组件
  • 配置了OpenTelemetry指标收集器
  • 启动了一个Gin HTTP服务器
  • 注册了/metrics端点,用于暴露Prometheus格式的指标数据
  • 启动了我们定义的API端点(如/hello)

4. 启动Prometheus监控服务

项目中已经包含了Prometheus的配置文件,使用Docker启动Prometheus服务:

docker compose up -d

这个命令会启动一个Prometheus容器,并使用项目中的prometheus.yml配置文件。Prometheus服务启动后,它会按照配置定期从我们的应用的/metrics端点拉取指标数据。

Prometheus的工作原理是定期"刮取"(scrape)目标服务的指标数据,然后存储在时间序列数据库中。这种Pull模式的采集方式有几个优点:

  • 简化了防火墙配置(只需要Prometheus能够访问目标服务)
  • 使得Prometheus能够检测到目标服务的健康状态
  • 减轻了目标服务的负担(由Prometheus控制采集频率)

5. 验证监控效果

现在,让我们来验证监控系统是否正常工作:

  1. 多次访问示例接口,产生一些监控数据:
curl http://localhost:8080/hello

每次调用这个接口,我们的应用都会增加一个名为api.counter的指标计数器。

  1. 查看原始指标数据:
curl http://localhost:8080/metrics

这个命令会显示当前应用暴露的所有指标数据,格式符合Prometheus的数据格式规范。你应该能看到类似下面的输出:

# HELP api_counter API调用的次数
# TYPE api_counter counter
api_counter{} 5 1620847200000

这个输出表明:

  • api_counter是一个计数器类型的指标
  • 它的描述是"API调用的次数"
  • 当前值是5(表示API已被调用5次)
  • 时间戳是1620847200000(这是一个Unix毫秒时间戳)
  1. 在Prometheus控制台查看可视化的指标数据:
    • 打开Prometheus界面:http://localhost:9090/graph
    • 在查询框中输入:api_counter_total
    • 点击"执行"按钮,你将看到类似下图的监控数据展示:

在这里插入图片描述

在Prometheus的Web界面中,你可以:

  • 查看指标随时间的变化趋势
  • 使用PromQL进行复杂的查询和数据分析
  • 创建自定义的监控面板
  • 设置告警规则(需要额外配置AlertManager)

代码详解

让我们深入了解这个监控系统的实现原理和核心代码。

项目结构

.
├── cmd
│   ├── import.gone.go  # 自动生成的组件导入文件
│   └── server.go       # 应用入口文件
├── controller
│   ├── ctr.go          # API接口定义和指标收集逻辑
│   └── init.gone.go    # 控制器初始化文件
├── docker-compose.yaml # Docker配置文件
├── prometheus.yml     # Prometheus配置文件
├── init.gone.go
└── module.load.go     # Gone模块加载配置

这种项目结构遵循了Gone框架的最佳实践,将不同功能模块清晰地分离。其中:

  • cmd目录包含应用的入口点和自动生成的组件导入文件
  • controller目录包含API控制器和路由定义
  • 根目录包含Gone框架的配置文件和Prometheus相关配置

核心组件说明

项目使用了goner/otel/meter/prometheus/gin组件,它提供了一个完整的指标监控解决方案,包括:

  • 自动创建一个HTTP服务(默认端口8080)
  • 提供/metrics接口用于暴露符合Prometheus格式的监控指标
  • 集成了OpenTelemetry指标收集和Prometheus导出功能
  • 基于Gin框架实现Web服务和路由管理

这个组件实际上是一个组合组件,它包含了以下功能模块:

  • goner/otel/meter/prometheus:Prometheus指标收集器和导出器
  • goner/otel/meter:OpenTelemetry指标管理核心功能
  • goner/otel:OpenTelemetry基础功能和配置
  • goner/gin:基于Gin的Web服务框架

通过Gone框架的依赖注入机制,这些组件被自动组合在一起,形成了一个完整的监控解决方案。

指标类型详解

在OpenTelemetry中,有四种基本的指标类型:

  1. Counter(计数器):只增不减的累计值,适用于记录事件发生的次数、请求数等。

    • 示例:API调用次数、错误发生次数
  2. Gauge(仪表盘):可增可减的瞬时值,适用于记录当前状态。

    • 示例:当前活跃连接数、当前内存使用量
  3. Histogram(直方图):记录观测值的分布情况,适用于分析数据的分布特征。

    • 示例:请求响应时间分布、文件大小分布
  4. Summary(摘要):类似于直方图,但直接计算分位数。

云原生内容分享(四):OpenTelemetry 重塑云原生的可观测性 随着云计算、微服务和容器化技术的快速发展,现代应用程序的架构变得越来越复杂。这种复杂性为运维人员和开发者带来了新的挑战,特别是在监控、追踪和诊断分布式系统的行为和性能方面。传统的监控工具和框架往往专注于单一的领域,如日志记录、度量收集或分布式追踪。然而,这些工具之间的互操作性和数据标准化程度有限,导致在大规模、多语言的云原生环境中实施全面可观测性变得困难且成本高昂。OpenTelemetry 与 Kubernetes 结合,构建了一套强大的云原生环境下的可观测性解决方案。 阅读详情

相关推荐

AI Agent可观测性实践:OTel与OpenLit技术解析

可观测性作为现代分布式系统的核心能力,通过日志、指标和追踪三大支柱实现系统内部状态的透明化。在AI Agent场景中,OpenTelemetry(OTel)作为CNCF标准观测框架,与OpenLit等专项工具结合,能有效解决LLM调用追踪、语义监控等独特需求。这种技术组合不仅提升故障诊断效率,更通过全链路数据关联支持智能决策优化,在金融风控、智能客服等对可靠性要求严苛的场景中尤为重要。实践表明,合理运用OTel的自动埋点与Elastic的AI分析能力,可将平均故障定位时间缩短90%以上。

weixin_28363123的博客 322

OpenTelemetry(OTel)简介与示例

应用层通过标准API和SDK生成格式统一的遥测数据。Collector通过可配置的管道(接收器 -> 处理器 -> 导出器)灵活、高效地收集和处理这些数据。整个过程通过上下文传播,将原本孤立的链路、指标和日志有机地关联起来。下面通过具体的代码示例,来演示OpenTelemetry(OTel)的自动埋点、手动埋点以及Collector的配置。

AI天才研究院 138

双向全桥隔离dc-dc变换器(DAB)

在全功率范围内,零电压开通优化调制方法能够实现双向全桥隔离dc-dc变换器所有开关的零电压开通,来提升变换器的效率,但其调制模式不连续。当变换器两端电压不匹配时,变换器的电流应力和回流功率将增加,软开关范围将变窄,变换器的效率显著降低。在轻载时,这两种电流优化调制方法采用相同的调制模式;在重载时,最小电流有效值调制方法具有两种调制模式,而最小电流应力调制方法只有一种。双向全桥隔离dc-dc变换器(DAB)和Buck/Boost不同,该变换器通过交流电感进行功率传输,其调制和控制不适合直接借鉴已有的研究。

m0_59329334的博客 3531

保姆级教程:在K230开发板上为张大头步进电机实现位置、速度、回零全功能控制

本文提供了一份详细的K230开发板与张大头步进电机的控制教程,涵盖硬件连接、协议解析、模块化代码实现及实战案例。通过位置、速度和回零功能的全面讲解,帮助开发者快速掌握步进电机的高级控制技术,适用于自动化、机器人等应用场景。

weixin_42629671的博客 710

Ubuntu系统下搭建QtCreator开发环境详细过程(Qt简介;Linux下安装QtCreator;Ubuntu安装后闪退蓝屏的解决方法)

Qt 是一个跨平台的 C++开发库。主要用来开发图形用户界面 (Graphical User Interface,简称 GUI) 程序。Qt 支持的操作系统有很多,例如通用操作系统 Windows、Linux、Unix、智能手机系统Android、ios、WinPhone, 嵌入式系统 QNX、VxWorks 等。

分享学习成长历程,Winter is coming! 8548

PX4 Gazebo仿真进阶:自定义飞机模型和地图的完整指南

本文详细介绍了如何在PX4 Gazebo仿真中自定义飞机模型和地图模型,包括文件架构解析、参数调整指南、地图定制技巧及常见问题解决方案。通过实战步骤和性能优化建议,帮助开发者高效完成复杂场景的飞行控制算法测试。

weixin_29081101的博客 385

Vue3 - 详细实现接入使用海康威视监控云端显示及控制功能,网站集成安装海康监控及实时监控显示、回放、放大缩小画面、视频画面截图、截屏抓拍、摄像头控制,实时推流及播放器解决延迟卡顿等问题(详细代码)

vue3,nuxt3,海康威视,海康监控,摄像头监控,实时监控画面,海康插件,视频播放,回放,视频流地址,取流协议,视频sdk播放,hik,rtsp,rtmp,hls,ws,网站,网页,h5页面,后台管理系统,vue3对接海康监控设备,无法连接,报错,vue3对接海康处理接口认证规则,监控实时预览和回放功能,海康api接口,安全认证,海康开放平台,官方文档,播放海康摄像头的监控视频,海康实时监控播放详细步骤,web,黑屏,特别卡不流畅怎么办,解决无法推流,录像,连接不上,vue3,海康,vue海康视频监控,

🏆 前端领域优质创作者 1万+

从0到1构建数据分析AI Agent:数据采集、洞察生成与报告自动化

在企业数字化转型的今天,数据分析师80%的时间被消耗在重复取数、数据清洗、报表排版等低价值劳动上,真正用于深度业务洞察和决策支持的时间不足20%。本文将从核心概念、技术原理、代码实现、落地实践全维度,教你从0到1搭建一套完整的数据分析AI Agent系统,实现「用户用自然语言提需求→Agent自动采集多源数据→AI自动挖掘业务洞察→自动生成符合业务要求的多格式报告」的全链路自动化,平均可将需求响应速度从天级压缩到分钟级,释放70%以上的分析师人力成本。

AI Python 编程之道的博客 472

无刷电机FOC硬件电路设计

本文先对MIT无刷电机FOC硬件电路分析做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章软件程序在我另外一个博客【嵌入式–伺服电机】MIT无刷电机FOC驱动程序分析。

qq_35635374的博客 574

Spring AI Alibaba Agent开发实战:DeepResearch Agent调度系统

智能Agent调度系统通过专业化分工解决传统AI系统的三大痛点:通用模型专业性不足、处理效率低下和资源浪费。系统采用分层架构设计,核心组件包括智能调度器、Agent选择辅助器和集成工具类,实现问题分类、专业Agent匹配和搜索策略优化的全流程自动化。系统特点包括:1)配置驱动的灵活扩展;2)分层回退的容错机制;3)支持工具调用与传统搜索的智能切换。该架构通过策略模式和依赖注入等技术,在保证稳定性的同时显著提升了研究质量和效率,为企业级多Agent协作提供了完整解决方案。(149字)

wjianwei666的专栏 501

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

上一篇: goner/otel 在Gone框架接入OpenTelemetry
下一篇: 降维打击——科普一下大刘小说中降维打击
dapeng-大鹏
博客等级 码龄16年 1349粉丝 116原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

dapeng-大鹏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值