在 Amazon 上设计带事件流式传输功能的数据网格用于提供实时推荐

cd9122f3638b0091fcb2c2005a0d293d.jpeg

f5f132280994985ea3084afc53124614.gif

点击上方【凌云驭势 重塑未来】

一起共赴年度科技盛宴!

本博文与 Federico Piccinini 联合撰写。

近年来,数据格局一直在发生变化:公司内部生产和使用大量数据的实体激增,对于大多数公司来说,制定合适的数据战略变得至关重要。现代化数据策略提供了全面的计划来管理、访问、分析和操作数据。因此,越来越多的公司正考虑采用数据网格架构,在这种新出现的范式中,数据按域进行划分,进一步明确了数据和技术堆栈的所有权,并实现了更灵活的设置。因此,要从数据网格架构中受益,您的一些应用程序在设计时可能需要按域分离数据。

在本文中,我们将向您展示如何针对需要实时推荐的场景设计数据网格架构。此推荐系统通过完全托管的机器学习(ML)服务 Amazon Personalize 实施,通过按域使用数据来发挥作用。对于推荐使用案例,务必要能够访问有关用户、项目和交互的信息,这些信息通常与公司内部的不同数据来源相关联。

Amazon Personalize:

https://aws.amazon.com/personalize/

由于 ML 应用程序可能有多种类型的输入数据,我们提出了一种既适用于静态数据,也适用于实时流式传输的解决方案。实时推荐需要流式传输数据,以符合用户的当前意图。

在文中,我们首先介绍数据网格范式,然后通过添加事件流式传输功能将其扩展到实时使用案例。我们以一家音乐流媒体公司为例,该公司为其客户提供收听点播歌曲的机会。该公司还通过同一平台开始提供点播播客,并希望利用现代化数据架构来支持数据访问,以实现快速的 ML 实验和推理。

01

数据网格:范式转变

域驱动设计(DDD)表示一种软件设计方法,该方法根据底层业务逻辑将复杂的解决方案划分为多个域。DDD 环境中经常提到的一种架构样式是微服务架构,在此概念中,软件系统被构建为松散耦合的实体,即微服务,每个实体为一个小团队所拥有,并围绕业务需求进行构建。这些范式加上云技术的进步,使公司能够更快地发布软件更新,并不断调整其技术堆栈以适应不断变化的业务需求。

不过,与软件架构不同的是,大多数数据架构仍然是围绕技术,而不是业务领域设计的。这种情况在 2019 年发生了改变,当时 Zhamak Dehghani 引入了数据网格。数据网格是一种范式转变,即转而将数据视作产品,并作为域的一部分进行处理。数据网格将 DDD 原则应用于数据架构:数据按照数据域来组织,并且将数据视作由团队所拥有并供使用的产品。这是从集中所有权模式向去中心化模式的转变,使得公司能够大规模访问数据。这种转变还允许每个团队在分配到某个数据域后,可以为自己的工作选择合适的技术来构建数据产品,就像在微服务上工作的软件工程师一样。

数据网格:

https://martinfowler.com/articles/data-monolith-to-mesh.html

数据网格倡导将数据管理系统的所有权和交付去中心化,同时强调对分布式治理和自助服务工具的需求。数据网格方法使数据域所有者具有更好的自主权,并将域汇集在一起,用于实现业务部门之间的数据共享和联合身份验证,同时不影响数据安全。这种架构类型可支持分布式数据概念,即拥有适当访问权限的人可以访问所有数据。数据湖与数据网格之间的一个关键区别是,在数据网格中,数据不必整合到单个数据湖中,可以保留在不同的数据库中。

有关采用数据网格作为域驱动数据架构的详细信息和优势详情,请参阅 Design a data mesh architecture using Amazon Lake Formation and Amazon Glue(使用 Amazon Lake Formation 和 Amazon Glue 设计数据网格架构)。

Design a data mesh architecture using Amazon Lake Formation and Amazon Glue:

https://aws.amazon.com/blogs/big-data/design-a-data-mesh-architecture-using-aws-lake-formation-and-aws-glue/

02

数据网格的组件

现在我们已充分了解了数据网格范式,下面来看一下其实施和组件。

首先,我们从数据生产者开始。数据生产者即负责维护、拥有和公开其域中特定数据的实体。由于域的分离,每个生产者可以独立选择自己的技术堆栈。

与此类似,我们还有数据使用者。顾名思义,这些组件使用生产者公开的一个或多个数据来源。和以前一样,采用数据网格架构意味着各个使用者彼此独立,也就是说他们可以实施不同的技术堆栈并解决不同的使用案例。

然后,静态数据层面由集中式数据目录完成,该组件充当生产者与使用者之间的纽带。此中间层负责将可用的数据生产者索引到集中式数据目录,并控制对不同数据来源的访问。

生产者使用数据目录,向组织中在使用者域工作的数据科学家和数据工程师公开数据产品(步骤 1a 和 1b)。下图说明了如何使得数据产品易于发现:在数据来源由其相应的生产者域注册到集中式目录中之后(步骤 1a 和 1b),数据使用者可以通过集中式数据目录查找感兴趣的数据来源(步骤 2a 和 2b)。

32e96990d04d31167f35255317e2887d.jpeg

03

处理实时事件

有人可能会争辩说,这种架构只能按原样支持静态数据。事实上,没有简单的解决方案可以将数据从生产者域实时移动到使用者。到目前为止,提出的范式解决了静态数据的情况,即生产者按需提取数据,而不是在数据发生变化时收到通知。

由于许多应用程序需要快速响应环境中发生的变化,因此,实时数据是数据架构中的一个重要考虑因素。例如,电子商务平台或视频流式传输服务,可以从用户与内容的实时交互中提取价值。在这些情况中,关键是要跟踪事件的发生,将其输入 ML 模型,然后相应地调整预测。

本部分将介绍一些可用于实施此模式的流平台,重点介绍 Apache Kafka,因为它使用频率高,而且许多公司正将其 Kafka 工作负载迁移到云端。

Apache Kafka:

https://kafka.apache.org/

Apache Kafka 是一个开源的分布式事件流平台,它可以从微服务或数据库等来源实时捕获数据,将事件存储在按主题组织的流中,并对这些事件做出实时反应和追溯性反应。基于 Apache Kafka 构建的事件流架构遵循发布/订阅范式:生产者通过写入操作将事件发布到主题,订阅这些主题的使用者则在事件发生时收到事件。在此场景中,可以用 Amazon Kinesis Data Streams 替代 Apache Kafka,前者是一种允许开发人员在云端收集、存储和处理实时数据的流服务。

Amazon Kinesis Data Streams:

https://aws.amazon.com/kinesis/data-streams/

以一个电子商务平台为例,我们可以有一个付款到微服务运行系统的支付功能,将事件发布到购买主题,并跟踪平台上发生的每笔交易。然后,我们可以让另一个组件订阅购买主题,以接收事件并采取相应的操作,例如更新商业智能控制面板。有关 Apache Kafka 的更多信息,我们建议您阅读 Introduction to Apache Kafka(Apache Kafka 简介)。

Introduction to Apache Kafka:

https://kafka.apache.org/intro

04

事件流架构

动态数据层面的引入是为了在数据网格环境中实施发布/订阅模式。这一层面由一组生产者和使用者域组成,这些域通过集中式事件流组件连接在一起,从而使实时事件可供访问。要从按域组织数据的架构中受益,我们认为每个生产者都应当有自己对应的集中式事件流,如下图所示。

您也可以将事件流视为向使用者发送实时事件的渠道,因此每个生产者都有自己的专用渠道来发送更新。

每个使用者可以根据特定的数据需求,订阅多个主题。当有新事件可用时,对应的生产者将其发布到相关的流中(步骤 1a 和 1b),订阅者可以读取事件(步骤 2a 和 2b),对其进行处理,然后采取相应的操作。

05dd2d5c42921252a060d3ee7c38b8e3.jpeg

上图显示了具有 N 个生产者域和 M 个使用者域的场景:每个使用者仅订阅该域中自己感兴趣的流。在此例中,使用者 #1 订阅了来自生产者 #1 的事件,而使用者 #M 同时订阅了来自生产者 #1 和生产者 #N 的事件。

您可以采用这种模式来满足多种使用案例和数据域的需求。例如,在音乐流媒体平台上播放歌曲的用户,可以生成一个新的事件,从交互服务生产者发送到个性化使用者,推荐系统在其中生成个性化推荐。同样,付款生产者可以发送交易请求,欺诈检测器使用者则确定交易是否存在欺诈。

为使生产者和使用者能够正确通信,事件负载架构必须保持一致。应用程序依赖于架构,因此对事件所做的任何更改都不会破坏生产者与使用者之间的隐性合约。对于复杂的使用案例,您可以使用架构注册表来在事件流中强制兼容性。有关使用 Amazon Glue 架构注册表选项的更多信息,请参阅 Validate streaming data over Amazon MSK using schemas in cross-account Amazon Glue Schema Registry(使用跨账户 Amazon Glue 架构注册表中的架构验证 Amazon MSK 上的流数据)。

Amazon Glue:

https://aws.amazon.com/glue

Validate streaming data over Amazon MSK using schemas in cross-account Amazon Glue Schema Registry:

https://aws.amazon.com/blogs/big-data/validate-streaming-data-over-amazon-msk-using-schemas-in-cross-account-aws-glue-schema-registry/

05

推荐使用案例

之前,我们介绍了数据网格架构背后的整体理念,但没有侧重于具体的使用案例。在本部分中,我们展示了一个使用 Amazon 实施网格范式的真实场景。

我们以音乐流媒体公司 XYZ 为例,该公司为其客户提供收听点播歌曲的机会。最近,XYZ 还开始通过同一平台提供点播播客。

ML 团队有意将播客添加到向用户呈现的个性化推荐目录中。为此,开发推荐系统(在数据网格范式中可视为使用者)的 ML 团队需要访问多个数据域(生产者):Users(用户)、Songs(歌曲)、Podcasts(播客)和 Interactions(交互)。

在本博文中,我们将 Amazon Personalize 用作完全托管的 ML 服务来提供个性化推荐。通过此服务,开发人员可训练、调整和部署自定义的 ML 模型,以提供高度定制的体验。Amazon Personalize 会配置基础设施并管理整个 ML 管道,包括处理数据、识别特征,以及训练、优化和托管模型。您可以在开发人员指南中了解有关 Amazon Personalize 的更多信息。

开发人员指南:

https://docs.aws.amazon.com/personalize/latest/dg/how-it-works.html

接下来,我们将针对静态数据和动态数据场景,更深入地研究解决方案的实施。ML 需要大量静态数据来创建数据集和训练模型。此外,个性化场景需要访问实时数据,以匹配用户的当前意图,因此我们需要访问实时事件和交互。此场景的数据网格解决方案需要以下两者:

静态数据 – 用户、项目和交互历史数据。其中一些可能存储在不同的系统和数据来源中。

动态数据 – 此数据用于实时事件,例如,收听的歌曲或目录中提供的新项目。

06

静态数据的架构

本部分重点介绍解决方案的静态数据部分。

下图显示了我们如何针对个性化推荐实施数据网格架构,并将播客纳入使用 Amazon Personalize 部署的推荐系统中。每个生产者域都拥有数据,并通过数据目录公开数据。使用者通过数据目录来查找应用程序所需的数据。

9cc52084017c3a2acdd8b1d49567fbb7.jpeg

首先,我们可以确定之前介绍过的网格架构的三个主要组件:数据生产者、集中式数据目录和数据使用者。

在这个具体例子中,我们可以看到不同的生产者域如何实施不同的存储解决方案:

● Users 域使用 Amazon Aurora 作为自己的业务部门(LOB, Line Of Business)数据库,即关系数据库(步骤 1a)

Amazon Aurora :

https://aws.amazon.com/rds/aurora/

● Songs 和 Podcasts 使用 NoSQL 数据库 Amazon DynamoDB(步骤 1b 和 1c)

Amazon DynamoDB:

https://aws.amazon.com/dynamodb/

● Interactions 将事件直接提取到 Amazon S3 中(步骤 1d)

生产者域使用 Amazon Simple Storage Service(Amazon S3),将其 LOB 数据库与数据目录分离。借助数据网格范式,每个生产者都将数据视为产品,因此可以在公开数据之前对其进行预处理,并以适合使用者的格式存储结果。此分离使用 Amazon Glue 来实施,用于定义提取、转换和加载(ETL)管道,结果最终存储在 S3 桶中(步骤 2a、2b、2c)。

Amazon Simple Storage Service:

http://aws.amazon.com/s3

Amazon Glue :

https://aws.amazon.com/glue/

最后,每个生产者通过集中式数据目录共享各自的 Amazon Glue Data Catalog(步骤 3a、3b、3c、3d)。

数据使用者现在可以通过集中式目录访问不同的数据域。如上图所示,我们有两个使用者:访问特定目录并在 Amazon QuickSight 控制面板上显示指标(步骤 4)的 Analytics 域,以及 Personalized Recommendations 域(步骤 5)。

Amazon QuickSight:

https://aws.amazon.com/quicksight/

后者是本文感兴趣的内容之一,它由一个  Amazon Glue ETL 作业组成,该作业通过集中式目录访问来自不同生产者的数据。ETL 作业执行传统的数据工程任务,例如,合并歌曲和播客数据。现在,我们可以生成自己的 Amazon Personalize 解决方案,其中项目数据集包含有关歌曲和播客的信息,扩展了最初的推荐目录。

然后,通过采用 Amazon API Gateway 部署的 API,我们的推荐引擎可用于推理请求(步骤 6)。

Amazon API Gateway:

https://aws.amazon.com/api-gateway/

该架构旨在跨多个账户运行:Amazon 账户是部署到其中的资源的自然边界,也是单个计费单位。此方法使我们能够分离不同域所拥有的资源,并保持运营灵活性:每个团队拥有并控制自己的账户。要详细了解在使用数据网格时在不同账户之间共享数据目录的方法,请查看 Design a data mesh architecture using Amazon Lake Formation and Amazon Glue(使用 Amazon Lake Formation 和 Amazon Glue 设计数据网格架构)。

Design a data mesh architecture using Amazon Lake Formation and Amazon Glue:

https://aws.amazon.com/blogs/big-data/design-a-data-mesh-architecture-using-aws-lake-formation-and-aws-glue/

现在,我们可以根据用户在歌曲和播客中的综合收听偏好,为其提供这两类推荐。在下一部分中,我们将探讨如何改进架构,以应对不断变化的数据,例如,添加到目录中的新歌曲或可用的新交互。

07

动态数据的架构

在前文中,我们针对数据网格环境中的事件流介绍了理论框架,定义为动态数据平面。接下来,我们可以深入研究具体使用案例的架构。

我们使用的场景包括四个生产者(Users(用户)、Songs(歌曲)、Podcasts(播客)和 Interactions(交互))、集中式流组件以及两个使用者域(Personalized Recommendations(个性化推荐)和 Analytics(分析))。动态数据层面通过使用事件流平台(即 Apache Kafka)来实施,每个生产者都有专门的流来发布其事件。

在音乐实时推荐场景中,Personalized Recommendations 使用者会收到有关 Users、Songs、Podcasts 和 Interactions 变更的通知。与静态示例类似,我们也考虑了第二个使用者域,名为 Analytics,用于创建有关交互趋势的实时控制面板。在这里,Analytics 使用者只需要交互事件,因而只订阅了 Interactions 流。

该架构旨在为生产者和使用者提供松散耦合的交互机制:生产者无需了解系统中的使用者。生产者专注于发布事件,将事件发送到动态数据层面,交付则由流媒体平台保证。

我们来深入了解在云端构建此架构的策略。为便于阅读,我们孤立地研究这部分解决方案,不添加到静态数据场景示意图中。

在技术方面,我们使用 Amazon Lambda 运行应用程序的后端业务逻辑:微服务在 Lambda 函数中运行逻辑,并将事件发布到事件流。我们之所以使用 Lambda,是因为它在可扩展性和运营效率方面都很适合我们的使用案例,而且它提供的运营开销最小。不过,该架构模式也可以使用其他类型的后端部署,例如,运行于 Amazon Elastic Kubernetes Service(Amazon EKS)或 Amazon Elastic Container Service(Amazon ECS)上的容器。

Amazon Lambda:

https://aws.amazon.com/lambda/

Amazon Elastic Kubernetes Service:

https://aws.amazon.com/eks/

Amazon Elastic Container Service:

https://aws.amazon.com/ecs/

动态数据层面使用 Amazon Managed Streaming for Apache Kafka(Amazon MSK)实现,这是一个完全托管的解决方案,用于在云端运行 Apache Kafka。它可预置服务器、配置 Apache Kafka 集群、在服务器出现故障时进行更换、编排服务器补丁和升级,并运行集群以实现高可用性。Kafka 组织事件并将其存储到主题中。主题始终为多生产者和多使用者:这意味着一个或多个生产者可以发布同一个主题,一个或多个使用者可以订阅以读取该主题。我们使用主题的概念为此架构范式建模,我们为每个生产者域分配一个主题。

Amazon Managed Streaming for Apache Kafka:

https://aws.amazon.com/msk/

最后,我们对之前介绍的使用者域 Personalized Recommendations 进行了调整,将实时事件考虑在内。这次,我们使用 Lambda 从主题读取事件,并调用命令以通过 Amazon Personalize 开发工具包调用 Amazon Personalize API。在同一个使用者域内,我们针对每个主题使用一个 Lambda 函数,一旦受监控主题中发布了新事件,该函数将立即触发。通过这种事件驱动的模式,我们可以在只有发布了新事件发布时才运行代码,并且我们需要更新 Amazon Personalize 中的信息。Personalized Recommendations 域中的每个 Lambda 函数,都使用 Amazon Personalize 开发工具包,在 Amazon Personalize 上调用相应操作并更新数据集

Amazon Personalize 开发工具包:

https://docs.aws.amazon.com/personalize/latest/dg/aws-personalize-set-up-sdks.html

数据集:

https://docs.aws.amazon.com/personalize/latest/dg/how-it-works-dataset-schema.html

我们使用下图来考虑系统中发生的新交互。该事件流模式的无服务器实施,扩展了数据网格以响应实时事件。

2dba694b2552bed86cfcb112eb2efb98.jpeg

运行应用程序后端逻辑的 Interactions 微服务,发布了一个新事件(步骤 1),该事件保留在 Interactions 主题中(步骤 2)。新事件的发布会触发订阅该主题的 Lambda 函数,在本例中为 InteractionsUpdate 和 InteractionsIngestor(步骤 3)。InteractionsUpdate 函数通过 Amazon Personalize 开发工具包,在 Amazon Personalize API 上调用 PutEvents 操作,将实时事件添加到推荐系统(步骤 4)。InteractionsIngestor 根据 Analytics 域采用的策略,触发操作以刷新控制面板。最后,其他服务和组件可以通过 Personalized Recommendation 域公开的 API 来使用推荐,从而使预测变得可用(步骤 5)。

对于我们为展示此架构的可扩展性而添加的 Analytics 域,我们使用 Lambda 函数将实时事件提取到 Amazon Kinesis Data Firehose 中。然后,我们可以结合使用 Amazon OpenSearch Service 和 Amazon QuickSight,将交互可视化。有关详细信息,请参阅 Visualize live analytics from Amazon QuickSight connected to Amazon OpenSearch Service(从连接到 Amazon OpenSearch Service 的 Amazon QuickSight 可视化实时分析)。

Amazon Kinesis Data Firehose:

https://aws.amazon.com/kinesis/data-firehose/

Amazon OpenSearch Service :

https://aws.amazon.com/opensearch-service/

Amazon QuickSight:

https://aws.amazon.com/quicksight/

Visualize live analytics from Amazon QuickSight connected to Amazon OpenSearch Service:

https://aws.amazon.com/blogs/big-data/visualize-live-analytics-from-amazon-quicksight-connected-to-amazon-opensearch-service/

由于数据生产者、Kafka 资源和数据使用者全都处在不同账户中,我们需要建立跨账户连接,将流量保持在 Amazon 基础设施内传输并避开公共互联网,这既是出于安全原因,也是出于成本优化考虑。本博文的目的是展示架构和实现此模式的方法。如果您希望在生产者和使用者与 Amazon MSK 之间建立跨账户连接,请参阅 Secure connectivity patterns to access Amazon MSK across Amazon Regions(访问 Amazon MSK 的安全连接模式)和 How Goldman Sachs builds cross-account connectivity to their Amazon MSK clusters with Amazon Private Link(Goldman Sachs 如何使用 Amazon Private Link 建立与 Amazon MSK 集群的跨账户连接)。

Secure connectivity patterns to access Amazon MSK across Amazon Regions:

https://aws.amazon.com/blogs/big-data/secure-connectivity-patterns-to-access-amazon-msk-across-aws-regions/

How Goldman Sachs builds cross-account connectivity to their Amazon MSK clusters with Amazon Private Link:

https://aws.amazon.com/blogs/big-data/how-goldman-sachs-builds-cross-account-connectivity-to-their-amazon-msk-clusters-with-aws-privatelink/

08

带事件流的数据网格:整合到一起

前面我们回顾了数据网格范式,并设计了一个解决方案,以强调将数据作为产品这一策略的重要性。每个生产者域都通过目录公开数据,并且这些数据可通过集中式数据目录 来集中发现。每个使用者域都有一个目录接口,用于连接到集中式目录,并查找构建该域所关注的解决方案所需的数据。

接下来,我们研究了动态数据的场景,引入了 Apache Kafka 和 Amazon MSK 来实施事件流平台,并通过 Lambda 将生产者和使用者与流媒体服务连接起来。这种事件驱动的实施方法,使我们能够将生产者与使用者分离,并使解决方案具有可扩展性,因为域可能会随着时间的推移发生变化,而无需对架构进行重大更改。

现在我们可以将它们整合到一起,如下图所示。带事件流架构的完整数据网格使用两个不同的数据面板:一个专用于共享静态数据(蓝色),另一个用于动态数据(红色)。

50d6c13ffddf01651f7d5b2f068a657f.jpeg

每个域都有与两个层面通信所需的两个接口:数据目录和 Lambda 函数。静态数据利用数据目录来进行共享和发现,而动态数据则由在生产者域中运行后端逻辑的服务发出。通过订阅到主题的 Lambda 函数来使用数据,这些函数部署在使用者域中。

09

结论

在本博文中,我们介绍了高级架构范式,您可通过该范式将数据网格的概念扩展到实时事件。

我们首先介绍与该架构样式相关的一些基本概念,然后展示了如何在 Amazon 多账户环境中应用该解决方案来解决实际业务挑战,例如,实时个性化推荐和分析。

此外,本文中介绍的框架可以推广到不同领域,例如,其他 Amazon AI 服务(如 Amazon Forecast 或 Amazon Comprehend),或您针对特定场景构建并通过 Amazon SageMaker 部署的自定义 ML 解决方案。Amazon 不仅具备最丰富的经验,还拥有最可靠、最具可扩展性和最安全的云,以及最全面的服务和解决方案,是发掘数据价值的最佳场所。

Amazon Forecast:

https://aws.amazon.com/forecast/

Amazon Comprehend:

https://aws.amazon.com/comprehend/
Amazon SageMaker:

https://aws.amazon.com/sagemaker/

更多资源:

● Amazon 数据网格帮助库

https://github.com/aws-samples/aws-data-mesh-utils

● 使用 Amazon Personalize 提供实时低延迟建议的参考架构和代码示例

https://github.com/aws-samples/personalization-apis

● Retail Demo Store

https://github.com/aws-samples/retail-demo-store

本篇作者

07e37e48550f9abe329f8e09da4974b8.png

Vittorio Denti

Vittorio Denti 是在伦敦工作的一位 Amazon 解决方案架构师。他在米兰理工大学(米兰)和瑞典皇家理工学院(斯德哥尔摩)获得了计算机科学与工程硕士学位,之后加入了 Amazon。Vittorio 具有分布式系统和机器学习背景,对云技术有着浓厚的兴趣。他尤其热衷于软件工程、构建 ML 模型以及将 ML 投入生产。

7e369bcd71f7a544c7cebe18425c3bc5.jpeg

Anna Grüebler

Anna Grüebler 是 Amazon 的专业解决方案架构师,专注于人工智能。她在帮助客户开发和部署机器学习应用程序方面拥有 10 多年的经验。她热衷于帮助其他人采用新技术,并利用在云端使用 AI 的优势来解决棘手的问题。

49be075609cb279a8e607c5dbf00fc00.png

2022亚马逊云科技 re:Invent 全球大会

精彩视频现已上线!

👇👇👇点击下方图片立即观看👇👇👇

1bc75468900554333a6efd8bae3a7100.jpeg

624a94d732ad0e21186bc462af936096.gif

6b9630cef99d76aaa2d6f2b53a36cbf3.gif

听说,点完下面4个按钮

就不会碰到bug了!

3ce9d56f4e32152f79c9d034989938a3.gif

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值