使用Amazon Kinesis Data Analytics Studio以交互方式查询Amazon MSK主题

493a091062d4f92b84ef7fd64dd60fcd.gif

通过 Amazon Kinesis Data Analytics Studio,您可以轻松地实时分析流式数据,并使用标准 SQL、Python 和 Scala 构建由 Apache Flink 提供支持的流式处理应用程序。只需在亚马逊云科技管理控制台上单击几下,就可以启动无服务器笔记本来查询数据流,只需几秒钟即可获得结果。Kinesis Data Analytics 降低了构建和管理 Apache Flink 应用程序的复杂性。Apache Flink 是一个用于处理数据流的开源框架和引擎。它具有高可用性和可扩展性,为流处理应用程序提供了高吞吐量和低延迟。

如果您运行 Apache Flink 工作负载,在不真正了解应用程序执行数据处理的步骤时,开发分布式流处理应用程序可能会遇到巨大的挑战。Kinesis Data Analytics Studio 将 Apache Zeppelin 笔记本的易用性与 Apache Flink 处理引擎的强大功能相结合,在完全托管式产品中提供高级流式分析功能。这加快了开发和运行连续生成实时洞察的流处理应用程序的速度。

在这篇文章中,我们将向您介绍 Kinesis Data Analytics Studio,以及如何使用 SQL、Python 和 Scala 开始对 Amazon Managed Streaming for Kafka(Amazon MSK)集群进行交互式数据查询。我们还会演示如何使用 Kinesis Data Analytics Studio 跨不同主题查询数据。Kinesis Data Analytics Studio 还与 Amazon Kinesis Data Streams、Amazon Simple Storage Service(Amazon S3)以及 Apache Flink 支持的各种其他数据源兼容。

先决条件

要开始使用,您必须满足以下先决条件:

  1.  一个 Amazon MSK 集群

  2.  用于将数据填充到 Amazon MSK 集群的数据生成器

要按照本指南操作并与流式数据进行交互,您需要一个有数据流经的数据流。

创建并设置 Kafka 集群

您可以使用 Amazon MSK 控制台或以下 Amazon Command Line Interface(Amazon CLI)命令创建 Kafka 集群。有关控制台说明,请参阅开始使用 Amazon MSK 和使用 Amazon MSK 创建 Studio 笔记本

您可以在 Amazon MSK 集群中创建主题和消息,也可以使用现有主题。

在这篇文章中,我们在Amazon MSK 集群中有两个主题:impressions 和 clicks,它们具有以下 JSON 格式的字段:

  • impressions – bid_id、campaign_id、country_code、creative_details、i_timestamp

  • clicks – correlation_id、tracker、c_timestamp

correlation_id 是 bid_id 的点击关联 ID,因此该字段对于我们在联接中使用的所有主题具有公用值。

对于Amazon MSK 主题中的数据,我们使用 Amazon MSK 数据生成器。有关设置和用法详细信息,请参阅 GitHub 存储库。(我们将在此博客中使用 adtech.json 示例)

下面是为 impressions 主题生成的示例 JSON 记录:

{
   "country_code": "KN",
   "creative_details": "orchid",
   "i_timestamp": "Sat Jul 10 05:34:56 GMT 2021",
   "campaign_id": "1443403873",
   "bid_id": "0868262269"
}
{
   "country_code": "BO",
   "creative_details": "mint green",
   "i_timestamp": "Sat Jul 10 05:34:56 GMT 2021",
   "campaign_id": "1788762118",
   "bid_id": "1025543335"
}

    *左右滑动查看更多

下面是为 clicks 主题生成的示例 JSON 记录:

{
   "c_timestamp": "Sat Jul 10 05:34:55 GMT 2021",
   "correlation_id": "0868262269",
   "tracker": "8q4rcfkbjnmicgo4rbw48xajokcm4xhcft7025ea1mt0htrfcvsgl1rusg8e8ez30p7orsmjx76vtrha2fi9qb3iaw8htd9uri9jauz64zdq8ldz7b0o8vzlkxs640hnwxgikpfvy5nno15c9etgrh79niku8hhtnxg94n03f2zci5ztv05jixu1r3p5yeehgm9kfd7szle9kikgo2xy5mlx09mmtlo9ndwqdznwjyj3yk02ufcwui1yvzveqfn"
}
{
   "c_timestamp": "Sat Jul 10 05:35:01 GMT 2021",
   "correlation_id": "0868262269",
   "tracker": "gfwq09yk0jwirg9mw60rrpu88h98tkd9xr645jsdoo7dwu24f8usha14uimtsfltvjmhl4i5rq24lz0aucqn6ji4da4xbo6db7lfezus7twhkw238dqw0pzdt98rn5lk8vf4tk6smkyyq38rhjaeh2ezsmlcg4v7im39u7knj10ofiint4fny0xcgqwta0uwq426oc21b1t8m446tmc6fyy7ops80xonzbzfc4a1xjd4x56x81uyg80dxyu2g7v"
}

*左右滑动查看更多

创建 Kinesis Data Analytics Studio 笔记本

您可以按照以下简单步骤开始与数据流进行交互:

1. 在 Amazon MSK 控制台上,选择 Process data in real time(实时处理数据)。

2. 选择 Apache Flink – Studio Notebook(Apache Flink – Studio 笔记本)。

3. 输入 Kinesis Data Analytics Studio 笔记本的名称,并允许笔记本创建 Amazon Identity and Access Management(IAM)角色。

在Amazon IAM 控制台上,您可以针对特定使用场景创建自定义角色。

4. 选择 Amazon Glue 数据库,用于存储笔记本使用的源和目标的相关元数据。

5. 然后选择 Create Studio notebook(创建 Studio 笔记本)。

我们保留应用程序的原定设置,并可以根据需要进行扩展。

6. 创建应用程序后,选择 Start(启动)来启动 Apache Flink 应用程序。

7. 完成后(需几分钟),选择 Open in Apache Zeppelin(在 Apache Zeppelin 中打开)。

要连接到 Amazon MSK 集群,您必须为 Kinesis Data Analytics Studio 笔记本指定在创建 Amazon MSK 集群时使用的相同 VPC、子网和安全组。如果您在设置过程中选择了 Process data in real time(实时处理数据),则系统已经为您完成了这些设置。

Studio 笔记本是使用笔记本的 IAM 角色创建的,该角色授予对 Amazon Glue 数据目录和表的必要访问权限。

示例应用程序

Apache Zeppelin 支持 Apache Flink 解释器,并允许在 Zeppelin 中直接使用 Apache Flink 进行交互式数据分析。在编写代码时,Flink 解释器支持三种语言:Scala、Python(PyFlink)和 SQL。笔记本需要在每个段落的顶部提供以下语言之一的规范,以正确解释语言:

  • %flink – Scala 环境 

  • %flink.pyflink – Python 环境

  • %flink.ipyflink – ipython 环境

  • %flink.ssql – 流式处理 SQL 环境

  • %flink.bsql – 批处理 SQL 环境

每个解释器还有其他几个预定义的变量,例如 Scala 中用于 StreamExecutionEnvironment 的 senv 变量,或者 Python 中具有同样用途的 st_env 变量。您可以查看这些入口点变量的完整列表。

在此部分中,我们展示了所有三种语言的相同示例代码,以重点介绍 Zeppelin 为您提供的开发灵活性。

SQL

我们使用 %flink.ssql(type=update) 标头告知笔记本,此段落需解释为 Flink SQL。我们在 Kafka 主题中创建两个表:

  •  impressions – 包含 bid_id、campaign_id、creative_details、country_code 和 i_timestamp 列,提供系统中展示次数的详细信息。

  •  clicks – 包含 correlation_id、tracker 和 c_timestamp 列,提供某个展示的点击详细信息。

这些表使用 Kafka 连接器,读取 us-east-1 区域中名为 impressions and clicks 的 Kafka 主题的最新偏移量。

在 Zeppelin 笔记本中运行此语句时,将会根据创建语句中指定的声明创建 Amazon Glue 数据目录表,这些表可立即用于来自 Amazon MSK 集群的查询。

如果 Amazon Glue 数据目录已包含该表,则无需完成此步骤。

%flink.ssql(type=update)
CREATE TABLE impressions (
bid_id VARCHAR,
creative_details VARCHAR(10),
campaign_id VARCHAR,
country_code VARCHAR(5),
i_timestamp VARCHAR,
serve_time as TO_TIMESTAMP (`i_timestamp`, 'EEE MMM dd HH:mm:ss z yyyy'),
 WATERMARK FOR serve_time AS serve_time -INTERVAL '5' SECOND
)
PARTITIONED BY (bid_id)
WITH (
'connector'= 'kafka',
'topic' = 'impressions',
'properties.bootstrap.servers' = '<bootstrap servers shown in the MSK client
info dialog>',
'format' = 'json',
'properties.group.id' = 'testGroup1',
'scan.startup.mode'= 'earliest-offset',
'json.timestamp-format.standard'= 'ISO-8601'
);

CREATE TABLE clicks (
correlation_id VARCHAR,
tracker VARCHAR(100),
c_timestamp VARCHAR,
click_time as TO_TIMESTAMP (`c_timestamp`, 'EEE MMM dd HH:mm:ss z yyyy'),
 WATERMARK FOR click_time AS click_time -INTERVAL '5' SECOND
)
PARTITIONED BY (correlation_id)
WITH (
'connector'= 'kafka',
'topic' = 'clicks',
'properties.bootstrap.servers' = '<bootstrap servers shown in the MSK client info dialog>',
'format' = 'json',
'properties.group.id' = 'testGroup1',
'scan.startup.mode'= 'earliest-offset',
'json.timestamp-format.standard'= 'ISO-8601'
);

*左右滑动查看更多

以下屏幕截图是 Amazon Glue 数据目录视图,其中显示了表示 Amazon MSK 主题的表

2959f3da46ed61cc76249f053028dbdb.png

在前面的几个表中,WATERMARK FOR serve_time AS serve_time - INTERVAL '5' SECOND 意味着我们可以容忍 5 秒时间范围内的无序事件传送,仍然可以产生正确的结果。

创建表后,运行一个查询,计算在 60 秒的翻转窗口中按照 campaign_id 和 creative_details 细分的展示次数:

%flink.ssql(type=update)
SELECT 
 campaign_id,
 creative_details,
 TUMBLE_ROWTIME(serve_time, INTERVAL '60' SECOND) 
   AS window_end, COUNT(*) AS c
FROM impressions
GROUP BY 
  TUMBLE(serve_time, INTERVAL '60' SECOND),
  campaign_id,
  creative_details
ORDER BY window_end, c DESC;

*左右滑动查看更多

此查询的结果在结果可用时立即显示。

eeea173ad40e951d5d88ccfda0a42e65.png

此外,我们希望查看展示的点击率:

SELECT 
  bid_id,
  campaign_id,
  country_code,
  creative_details,
  CAST(serve_time AS TIMESTAMP) AS serveTime,
  tracker,
  CAST(click_time AS TIMESTAMP) AS clickTime,
  CASE
     WHEN `click_time` IS NULL THEN FALSE
     WHEN `click_time` IS NOT NULL THEN TRUE
  END AS clicked
FROM  impressions 
LEFT OUTER JOIN clicks 
  ON bid_id = correlation_id AND
  click_time BETWEEN  serve_time AND 
  serve_time + INTERVAL '2' MINUTE ;

*左右滑动查看更多

此查询会为每个展示生成一行,并将其与投放广告后 2 分钟内观察到的点击(如果有)进行匹配。这实际上是在跨主题执行联接操作以获取此信息。

b2050d721fd047f4297834341bcb9597.png

您可以使用以下代码,将这些数据插回到现有的 Kafka 主题中:

SELECT 
INSERT INTO clickthroughrate 
SELECT 
  bid_id,
  campaign_id,
  country_code,
  creative_details,
  CAST(serve_time AS TIMESTAMP WITHOUT TIME ZONE) AS serveTime,
  tracker,
  CAST(click_time AS TIMESTAMP WITHOUT TIME ZONE) AS clickTime,
  CASE
     WHEN `click_time` IS NULL THEN FALSE
     WHEN `click_time` IS NOT NULL THEN TRUE
  END AS clicked
FROM  impressions 
LEFT OUTER JOIN clicks 
  ON bid_id = correlation_id AND
  click_time BETWEEN  serve_time AND 
  serve_time + INTERVAL '2' MINUTE ;

*左右滑动查看更多

在数据目录中,为 Kafka 主题创建相应的表(如果尚不存在)。运行上述查询后,您可以在 Amazon MSK 主题中看到数据(请参阅下面的示例):

1095810839,1911670336,KH,"mint green","2021-06-15 01:08:00","ainhpsm6vxgs4gvyl52v13s173gntd7jyitlq328qmam37rpbs2tj1il049dlyb2vgwx89dbvwezl2vkcynqvlqfql7pxp8blg6807yxy1y54eedwff2nuhrbqhce36j00mbxdh72fpjmztymobq79y1g3xoyr6f09rgwqna1kbejkjw4nfddmm0d56g3mkd8obrrzo81z0ktu934a00b04e9q0h1krapotnon76rk0pmw6gr8c24wydp0b2yls","2021-06-15 01:08:07",true
0946058105,1913684520,GP,magenta,"2021-06-15 01:07:56","7mlkc1qm9ntazr7znfn9msew75xs9tf2af96ys8638l745t2hxwnmekaft735xdcuq4xtynpxr68orw5gmbrhr9zyevhawjwfbvzhlmziao3qs1grsb5rdzysvr5663qg2eqi5p7braruyb6rhyxkf4x3q5djo7e1jd5t91ybop0cxu4zqmwkq7x8l7c4y33kd4gwd4g0jmm1hy1df443gdq5tnj8m1qaymr0q9gatqt7jg61cznql0z6ix8pyr","2021-06-15 01:08:07",true
0920672086,0888784120,CK,silver,"2021-06-15 01:08:03","gqr76xyhu2dmtwpv9k3gxihvmn7rluqblh39gcrfyejt0w8jwwliq24okxkho1zuyxdw9mp4vzwi0nd4s5enhvm2d74eydtqnmf7fm4jsyuhauhh3d32esc8gzpbwkgs8yymlp22ih6kodrpjj2bayh4bjebcoeb42buzb43ii1e0zv19bxb8suwg17ut2mdhj4vmf8g9jl02p2tthe9w3rpv7w9w16d14bstiiviy4wcf86adfpz378a49f36q","2021-06-15 01:08:16",true

*左右滑动查看更多

这是来自前述查询的 CSV 数据,其中显示了展示的 ClickThroughRate。您可以使用这种机制,将数据从 Flink 直接永久存储回 Kafka 中。

Scala

我们使用 %flink 标头来说明此代码块将通过 Scala Flink 解释器进行解释,并创建一个与 SQL 示例中相同的表。但是,在本例中,我们使用 Scala 解释器的内置流式表环境变量 stenv 来运行 SQL DDL 语句。如果该表已存在于 Amazon Glue 数据目录中,则此语句将发出错误,说明该表已存在。

%flink 
stenv.executeSql("""CREATE TABLE impressions (
  bid_id VARCHAR,
  creative_details VARCHAR(10),
  campaign_id VARCHAR,
  country_code VARCHAR(5),
  i_timestamp VARCHAR,
  serve_time as TO_TIMESTAMP (`i_timestamp`, 'EEE MMM dd HH:mm:ss z yyyy'),
  WATERMARK FOR serve_time AS serve_time -INTERVAL '5' SECOND
  )
  WITH (
  'connector'= 'kafka',
  'topic' = 'impressions',
  'properties.bootstrap.servers' = '< Bootstrap Servers shown in the MSK client info dialog >',
  'format' = 'json',
  'properties.group.id' = 'testGroup1',
  'scan.startup.mode'= 'earliest-offset',
  'json.timestamp-format.standard'= 'ISO-8601'
  )""")

stenv.executeSql("""
 CREATE TABLE clicks (
 correlation_id VARCHAR,
 tracker VARCHAR(100),
 c_timestamp VARCHAR,
 click_time as TO_TIMESTAMP (`c_timestamp`, 'EEE MMM dd HH:mm:ss z yyyy'),
 WATERMARK FOR click_time AS click_time -INTERVAL '5' SECOND
 )
 WITH (
 'connector'= 'kafka',
 'topic' = 'clicks',
 'properties.bootstrap.servers' = '< Bootstrap Servers shown in the MSK client info dialog >',
 'format' = 'json',
 'properties.group.id' = 'testGroup1',
 'scan.startup.mode'= 'earliest-offset',
 'json.timestamp-format.standard'= 'ISO-8601'
 )""")

*左右滑动查看更多

在 Scala 表 API 中执行翻转窗口时,首先需要定义对我们创建的表的内存中引用。我们使用 stenv 变量,通过 from 函数并引用表名来定义此表。创建完成后,我们可以通过 serve_time 列,创建 1 分钟数据的窗口化聚合。请参阅以下代码:

%flink
val inputTable: Table = stenv.from("impressions")
val tumblingWindowTable = inputTable.window(Tumble over 1.minute on $"serve_time" as $"oneMinuteWindow")
.groupBy( $"oneMinuteWindow", $"campaign_id",$"creative_details")
.select($"campaign_id", $"creative_details", $"oneMinuteWindow".rowtime as "window_end",$"creative_details".count as "c")
使用 ZeppelinContext 将笔记本中的 Scala 表聚合可视化:
%flink
z.show(tumblingWindowTable, streamType="update")

*左右滑动查看更多

以下屏幕截图显示了结果。

0858802c676da119e9e76a7c1947ca34.png

2a7446ac567d2948b4b855dddc730d1a.png

此外,我们希望通过与点击次数联接来查看展示的点击率:

val left:Table = stenv.from("impressions").select("bid_id,campaign_id,country_code,creative_details,serve_time")
val right:Table = stenv.from("clicks").select("correlation_id,tracker,click_time")
val result:Table = left.leftOuterJoin(right).where($"bid_id" === $"correlation_id" && $"click_time" < ( $"serve_time" + 2.minutes) && $"click_time" > $"serve_time").select($"bid_id", $"campaign_id", $"country_code",$"creative_details",$"tracker",$"serve_time".cast(Types.SQL_TIMESTAMP) as "s_time", $"click_time".cast(Types.SQL_TIMESTAMP) as "c_time" , $"click_time".isNull.?("false","true") as "clicked" )

*左右滑动查看更多

使用 ZeppelinContext 将笔记本中的 Scala 表聚合可视化。

z.show(result, streamType="update")

*左右滑动查看更多

以下屏幕截图显示了结果。

e43301444bd692763b71b00b28e9c57b.png

784c6817d830808d92cc3a9ac266b1e3.png

Python

我们使用 %flink.pyflink 标头来说明此代码块将通过 Python Flink 解释器进行解释,并创建一个与 SQL 和 Scala 示例中相同的表。在本例中,我们使用 Python 解释器的内置流式表环境变量 st_env 来运行 SQL DDL 语句。如果该表已存在于 Amazon Glue 数据目录中,则此语句将发出错误,说明该表已存在。

%flink.pyflink
st_env.execute_sql("""
 CREATE TABLE impressions (
 bid_id VARCHAR,
 creative_details VARCHAR(10),
 campaign_id VARCHAR,
 country_code VARCHAR(5),
 i_timestamp VARCHAR,
 serve_time as TO_TIMESTAMP (`i_timestamp`, 'EEE MMM dd HH:mm:ss z yyyy'),
 WATERMARK FOR serve_time AS serve_time -INTERVAL '5' SECOND
 )
 WITH (
 'connector'= 'kafka',
 'topic' = 'impressions',
 'properties.bootstrap.servers' = '< Bootstrap Servers shown in the MSK client info dialog >',
 'format' = 'json',
 'properties.group.id' = 'testGroup1',
 'scan.startup.mode'= 'earliest-offset',
 'json.timestamp-format.standard'= 'ISO-8601'
 )""")

st_env.execute_sql("""
 CREATE TABLE clicks (
 correlation_id VARCHAR,
 tracker VARCHAR(100),
 c_timestamp VARCHAR,
 click_time as TO_TIMESTAMP (`c_timestamp`, 'EEE MMM dd HH:mm:ss z yyyy'),
 WATERMARK FOR click_time AS click_time -INTERVAL '5' SECOND
 )
 WITH (
 'connector'= 'kafka',
 'topic' = 'clicks',
 'properties.bootstrap.servers' = '< Bootstrap Servers shown in the MSK client info dialog >',
 'format' = 'json',
 'properties.group.id' = 'testGroup1',
 'scan.startup.mode'= 'earliest-offset',
 'json.timestamp-format.standard'= 'ISO-8601'
 )""")

*左右滑动查看更多

在 Python 表 API 中执行滑动(跳跃)窗口时,首先需要定义对我们创建的表的内存中引用。我们使用 st_env 变量,通过 from_path 函数并引用表名来定义此表。创建完成后,我们将创建 1 分钟数据的窗口化聚合,根据 event_time 列每五秒发送一次结果。请参阅以下代码:

%flink.pyflink

input_table = st_env.from_path("impressions")
tumbling_window_table =(input_table.window(Tumble.over("1.minute").on("serve_time").alias("one_minute_window"))
.group_by( "one_minute_window, campaign_id, creative_details")
.select("campaign_id, creative_details, one_minute_window.end as window_end, creative_details.count as c"))

*左右滑动查看更多

使用 ZeppelinContext 将笔记本中的 Python 表聚合可视化。

%flink.pyflink

z.show(tumbling_window_table, stream_type="update")

*左右滑动查看更多

以下屏幕截图显示了结果。

e1d981776a140d718014d9047486adcb.png

694d98080bad7f67293f50d7028f0c34.png

此外,我们希望通过与点击次数联接来查看展示的点击率:

impressions = st_env.from_path("impressions").select("bid_id,campaign_id,country_code,creative_details,serve_time")
clicks = st_env.from_path("clicks").select("correlation_id,tracker,click_time")
results = impressions.left_outer_join(clicks).where("bid_id == correlation_id && click_time < (serve_time + 2.minutes) && click_time > serve_time").select("bid_id, campaign_id, country_code, creative_details, tracker, serve_time.cast(STRING) as s_time, click_time.cast(STRING) as c_time, (click_time.isNull).?('false','true') as clicked")

*左右滑动查看更多

扩展

一个 Studio 笔记本由一个或多个任务组成。您可以将一个 Studio 笔记本任务拆分为多个并行实例来运行,其中每个并行实例处理任务数据的一个子集。任务的并行实例数称为并行度,调整该数字有助于更高效地运行任务。

在创建时,Studio 笔记本将获得四个并行的 Kinesis 处理单元(KPU,Kinesis Processing Unit),它们构成了应用程序的并行度。要增加并行度,请浏览到 Kinesis Data Analytics 控制台,选择应用程序名称,然后选择 Configuration(配置)选项卡。

5ceb81202059e91a8481f0c1ef5b0060.png

在此页面的 Scaling(扩展)部分中,选择 Edit(编辑)并修改 Parallelism(并行度)条目。除非您的应用程序受限于 I/O,否则我们不建议将 Parallelism Per KPU(每 KPU 的并行度)设置提高到 1 以上。

选择 Save changes(保存更改),以增加或减少应用程序的并行度。

清理

您可能需要在完成后清理演示环境,为此,请停止 Studio 笔记本,然后删除为数据生成器和 Amazon MSK 集群创建的资源(如果您创建了新集群)。

小结

利用 Kinesis Data Analytics Studio,可以大大加快使用 Apache Flink 开发流处理应用程序的速度,它提供了丰富的可视化效果、用于开发管道的可扩展且用户友好的界面以及灵活的语言选择,为任意流式处理工作负载提供高性能和强大功能。您可以在笔记本中运行段落,也可以将 Studio 笔记本提升到具有持久状态的 Kinesis Data Analytics for Apache Flink 应用程序中,如本文中的 SQL 示例所示。

有关更多信息,请参阅以下资源:

· Amazon Kinesis Data Analytics 功能

· 将 Studio 笔记本用于 Kinesis Data Analytics for Apache Flink

· Apache Flink 文档

· 适用于 Apache Zeppelin 的 Flink 解释器

本篇作者

46de2864c12a656e78fb7dd14f714c22.png

Chinmayi Narasimhadevara

解决方案构架师

在 亚马逊云科技主要从事大数据和分析工作。Chinmayi 拥有超过 15 年的信息技术行业从业经验。她帮助亚马逊云科技客户构建具备高可扩展性和高性能的高级解决方案

617f1fcca9f7fb240db5052acd783818.gif

4fd007e17386da3f467525944b4ed854.gif

听说,点完下面4个按钮

就不会碰到bug了!

d57f6892fd6195cc1d3797143b2eed98.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值