RTB 引擎技术白皮书

1、引言

        RTB全称“Real Time Bidding”,是一种利用第三方技术在数以百万计的网站上针对每一个用户展示行为进行评估以及出价的竞价技术。与大量购买投放频次不同,实时竞价规避了无效的受众到达,只针对有意义的用户进行购买。对于媒体来说,可以带来更多的广告销量、实现销售过程自动化及减低各项费用的支出。而对于广告商和代理公司来说,直接的好处就是提高了效果与投资回报率。RTB广告的特点在于,广告平台售卖的不仅仅是传统意义上的广告位,而是访问这个广告位的具体用户,这个用户会有自己的兴趣爱好,广告能够投其所好产生最大的收益。

2、系统概述

        竞价引擎是投放平台的核心组件,负责受理竞价请求并返回竞价结果,在竞价过程中需要充分考虑媒体、用户、广告三者关系,兼顾展示点击预测值进行估价,在尽力控制好成本的基础上提高竞价成功数。

2.1、项目背景

        2002年到2011年,搜索引擎关键字广告快速增长,与展示广告两分天下。从2011年开始,互联网广告行业发生新的变化,展示广告领域出现实时竞价RTB技术,传统方式已无法满足广告主精细化要求。当前国内已经诞生四十多家互联网广告 DSP,RTB广告占互联网广告比约0.5%,预计将成为与搜索引擎关键字广告并驾齐驱的一个新的增长点。

2.2、编写目的

        本文档详细描述了投放平台中竞价引擎的设计思想、业务流程和实现细节,描述了组件模块划分及逻辑搭配组合,提供相关人员以指导,有助于阅读理解和梳理清楚功能代码和调用关系。本文档的适用人群为系统架构师、开发工程师和测试工程师。

2.3、名词解释

        投放平台:投放平台完成包括与完成供应方平台数据交互、日志存储等一系列事物,由适配器、竞价引擎、日志系统共同组成。

        适配器:受理供应方平台的以TCP作为传输层承载协议,基于标准HTTP应用层协议,采用开放式私有协议封装数据的竞价请求报文,通过投放平台内部传输协议完成供应方平台与竞价引擎之间的数据交互。

        竞价引擎:大并发、高响应、策略匹配与估值计算的实施业务处理系统。

        日志系统:完整记录投放平台的数据流过程与结果,将数据写入系统总线以便供后端进行离线数据分析与展示。

        竞价请求:由供应方平台按照事先约定好协议发送过来的,包含用户、广告位等信息的竞价请求报文。

        出价应答:由本系统经过一系列策略匹配与估值计算后,向供应方平台按约定协议返回对应竞价请求的响应报文。

        有效应答:符合定义策略并满足估值要求,向供应方平台提交广告内容与有效出价的响应报文。

        无效应答:未满足策略或估值不符,向供应方平台提交不包含广告信息和零报价的响应报文。

3、任务概述

        本系统是一个大并发、高响应、快速处理的实施系统,因此系统的设计与实现需要满足以下技术指标与业务要求。

3.1、业务指标

        单台设备的业务处理能力不低于每秒15000次QPS;单笔业务处理时间小于5毫秒;广告订单并发数最大值为1000个;广告订单排期最大值360天;每个订单绑定广告位总数最大值2000个;用户历史行为最大值200个关键字和200个关键网站;广告位CTR预测值总数最大值为100000个;

3.2、运行环境

        平板服务器或刀片组服务器

        X86多CPU及多核CPU,内存不小于32G,千兆网卡。

        64位操作系统:Red Hat Linux、CentOS。

  1. 系统概述

        系统总体结构由适配器、竞价引擎、日志系统、iCache几大子系统组成,分别承担平台接入、广告竞价、日志存储和精准服务的功能。平台接入作为系统总入口,负责与供应方平台的媒体对接,接收媒体下发的竞价请求,并在最短的时间内将响应应答返回供应方平台,各供应方平台的传输协议各不相同,存在较大的差异,;竞价引擎是RTB的核心子系统,负责广告维度、参数匹配、精普分离、精普筛选、估值预测、动态选单等一系列计算过程,从众多广告订单中,筛选出最符合该竞价请求的投放单,要求竞价过程准确快速;日志系统负责将竞价请求、出价应答、出价结果等信息以日志的形式保存到后台日志分析系统中,按照各维度统计汇总竞价过程;iCache系统负责提供精准服务,接受竞价引擎对用户私有属性的查询操作,为精准广告的筛选提供数据依据。

4.1、流程步骤

  1. 供应方平台向适配器下发包含用户标识和广告位信息的竞价请求;
  2. 适配器解析供应方平台解析,并按照RTB系统内部通信协议进行封装和压缩,发送到竞价引擎设备。
  3. 竞价引擎设备解析内部通信协议,获取供应发平台下发的参与竞价的各字段信息。
  4. 根据供应方平台下发的用户标识(UID、UA、用户IP)信息,从iCache系统查询该用户的历史行为信息。
  5. 根据iCache查询结果执行普通投放和精准投放广告单的集合分离,再经过一系列处理与计算,得到有效或空竞价结果,并返回竞价引擎。
  6. 适配器按照内部通信协议解析竞价结果,并按照供应方平台协议还原,返回应答。

4.2、拓扑图示

4.3、序列图示

        下图描述了从需求方平台到适配器、竞价引擎,以及周边业务支撑系统的相互序列关系。一个完整的竞价过程应该包括下图所示的各个步骤,从而达到稳定高效的运行,以及对大并发快速响应的实时目标。一切流程均是围绕竞价引擎产生且终止于竞价引擎,同时表现出对竞价引擎的强依赖性。

5、详细设计

        该系统是一个后台守护、实时处理、大并发数、高速响应、同步处理、状态监控的多线程服务系统。系统启动之初完成若干步骤的初始化,创建监控线程、创建工作线程,在阻塞模式的主套接字上挂起等待。传输层采用UDP协议,所有工作线程均平等拥有从套接字读取数据的权利。读取的数据提交给协议解析模块,得到所有参与竞价过程的字段。之后经过一个复杂的计算过程和再次封装的过程,返回最终结果。

5.1、主体流程

5.1.1、启动流程

        启动流程由入口参数解析、设置信号处理函数、配置文件读取、内存映射、创建子进程、创建监控线程、创建套接字和创建工作线程八个步骤组成,流程如上图左侧示意。

  1. 启动参数解析获取,得到配置文件加载路径、主进程监听端口、工作线程总数、初始化标识、缓存加载标识,以及帮助、版本等辅助信息。
  2. 设置信号处理函数,对内存非法操作等进行捕获,并调用相关处理函数。
  3. 读取配置文件,从配置文件中读取可变动配置信息,用于进一步加载相关详细配置。
  4. 内存申请、初始化或映射,根据启动参数,对内存数据进行操作,操作包括开辟内存空间、加载数据和映射内存空间到进程中地址。
  5. 创建子进程,使父进程退出,从而达到将进程放置到后台执行的目的。
  6. 创建监控线程,用于定时监控配置信息的变更,当数据发生变化的时候,执行重新加载过程。
  7. 创建套接字,在指定端口进行监听。
  8. 创建工作线程,在套接字接收函数上挂起等待。

5.1.2、配置读取

        配置文件路径有入口参数指定,通过读取配置文件中各字段内容,加载那些经常变动,同时对运行结果产生影响的信息,流程如上图中间示意。

  1. 打开配置文件。
  2. 解析设备编号。
  3. 解析默认CTR值。
  4. 解析套接字超时值,单位为微秒。
  5. 解析GtKv缓存系统的地址和端口。
  6. 解析广告配置文件路径。
  7. 解析IP地域库存储路径。
  8. 解析CTR文件存储路径。
  9. 解析广告配置文件变更标识文件路径。

10、解析竞价引擎日志文件存储路径和文件名。

11、解析iCache系统所有设备的地址和端口。

5.1.3、配置加载

        配置加载是对广告执行单处理计算过程中的中间数据进行内存写入操作,在执行单选单的过程中,通过一系列的内存操作完成匹配算法操作,流程如上图右侧示意。

  1. 根据入口参数对GtKv系统进行初始化操作。
  2. 初始化全局线程读写锁,用于解决配置加载期间数据同步问题。
  3. 从广告配置文件中读取广告基础性配置信息加载到内存中。
  4. 根据广告配置信息初始化广告投放效果数据到内存中。
  5. 读取地域库数据,将IP地址与地狱关联关系加载到内存中。
  6. 读取CTR预测值文件,将广告位ID、物料ID与展示点击值对应关系加载到内存中。
  7. 初始化用户频次数据,并对该空间做复位操作。
  8. 广告位频次控制初始化,初始值为零。
  9. 关键字初始化操作是创建一棵AC树,将关键字和关键网站加载到树中。

10、初始化倒排索引,主键是可枚举广告字段、值为广告执行单ID集合。

11、根据iCache设备地址创建一致性哈希列表。

        倒排索引列表包括Ban(敏感类别)索引、Tim(投放时间)索引、Lea(投放平台)索引、Pid(广告位ID)索引、Are(投放区域)索引、Mop(网络类型)索引、Dev(设备类型)索引、Con(视频格式)索引。倒排索引采用红黑树存储,每条索引为一个单独节点。

5.2、监控线程

        监控线程用于检查配置变更情况,当当前日期、广告配置文件、ctr预测文件发生变化的时候,许多对配置进行调整,调整包括复位擦除、保留追加和全量加载等操作。线控线程核心处理是一个无限循环过程,每间隔一定时间(宏定义),启动一次检查过程。

        当前日期变更:日期发生变化,读取广告配置信息并重新加载、重新创建倒排索引和热键AC树。对展示点击数进行复位操作,并重新定义广告下辖展示点击数结构。

        广告配置变更:检查广告配置标识文件的时间戳,与上次更新时间戳进行对比,重新加载广告配置信息和重建倒排索引和热键AC树。

        CTR配置变更:当CTR配置文件的时间戳与上次更新时间不符的时候,读取配置文件,加载到内存容器中。

5.3、工作线程

        工作线程在监听套接字上以阻塞模式接收报文,将数据提交给内部协议解析模块,根据报文头部业务属性字段判断是否进行解压缩处理,最终提取到参与竞价的所有字段。接下来调用竞价计算处理函数,从众多候选广告执行单中完成筛选过程。对于有效的竞价结果按照内部通信协议封装后和压缩处理后返回适配器,无效的竞价结果只填充报文头部和无效原因后返回适配器。至此一个竞价流程结束,线程重新在报文接收处挂起。

5.3.1、工作流程

        工作流程完成从数据接收、竞价计算、数据返回的完整过程,所有处理均在本线程内完成,流程如上图左侧示意。

  1. 接收适配器下发的UDP数据报文。
  2. 根据内部通信协议对头部、体部解析,根据业务属性执行解压缩处理。
  3. 将体部所有字段值解析到数据结构中,用于接下来的竞价计算处理。
  4. 调用竞价计算处理函数,得到有效出价的广告执行单或无效的竞价结果。
  5. 按照内部通信协议对有效出价的广告单进行字段封装,根据业务属性执行压缩处理。
  6. 无效竞价结果生成内部通信协议头部,并对RESULT字段填充无效原因。
  7. 向适配器返回竞价结果的应答报文。

5.3.2、竞价流程

        竞价流程负责从众多广告候选执行单中,筛选出最符合当前竞价策略的广告,和RTB实时竞价核心业务中的核心,流程如上图中间示意。

  1. 创建普通投放和精准投放广告候选集合。
  2. 将用户的IP地址转换成地域信息,后面用于区域投放的匹配。
  3. 使用竞价请求各字段进行维度计算。维度计算的原则是广告配置中可枚举字段作为维度主键,执行单ID集合作为值。
  4. 经过维度计算以后,结果集合非空的情况下,则进行参数计算。参数计算的原则是对广告配置中不可枚举的字段进行遍历检查。
  5. 经过参数计算以后,结果集合依旧非空的情况下,进行普通投放和精准投放的分离操作,判断的依据是检查该广告执行单下时候绑定了关键字和关键网站。
  6. 按照精准优先普通的原则,执行精准广告执行单的筛选的操作。
  7. 如果精准广告执行单筛选失败,则进行普通广告执行单的筛选操作。

5.3.3、维度计算

        维度计算是对广告执行单中可枚举字段执行倒排索引的查找操作,将符合条件的执行单ID保存到临时结果集合中。结果集合中的数据机构为执行单ID加维度,在所有维度计算结束以后,对结果集合中的广告执行单进行维度的匹配度进行验证。当且仅当所有维度均成功匹配的情况下,方认为符合维度条件,否则从结果集合中剔除。流程如上图右上小图示意。

  1. 进行时间维度的计算,将满足当前投放时间的广告执行ID集合保存到结果集合中。
  2. 进行平台维度的计算,将设置在该平台投放的所有广告执行单ID集合保存到结果集合中。
  3. 进行广告位维度的计算,将绑定该广告位和绑定全体广告位的所有广告执行单ID保存到结果集合中。
  4. 进行地域维度的计算,将绑定该地域和绑定全体地域的所有广告执行单ID保存到结果集合中。
  5. 如果竞价请求的流量来自于移动,则对移动维度进行计算,并将结果保存到集合中。
  6. 检查竞价请求中敏感类别,如果符合则从结果集合中将广告执行单ID从结果集合中剔除。
  7. 检查竞价请求中广告类别,如果符合则从结果集合中将广告执行单ID从结果集合中剔除。
  8. 检查竞价请求中创意类别,如果符合则从结果集合中将广告执行单ID从结果集合中剔除。
  9. 遍历筛查结果集合中的所有广告执行单的维度,对于维度不满足的从结果集合中剔除。

5.3.4、参数计算

        参数计算是对广告执行单中不可枚举字段进行逐一对比,不满足的从结果结合中剔除。流程如上图右下小图示意。

  1. 从结果集合中提取一个广告执行单。
  2. 对比该广告执行单的广告尺寸和竞价请求中下发的广告尺寸是否一致。不满足要求的从结果集合中剔除。
  3. 对比该广告执行单的最高出价和竞价请求中下发的媒体最低限价,小于的话则从结果集合中剔除。
  4. 查看该广告执行单的投放情况,投放金额、展示数或点击数超出计划值则从结果集合中剔除。
  5. 查看该用户的该广告执行单的投放数,如果超过计划数则从结果集合中剔除。
  6. 查看该广告位的投放数,如果超过计划数则从结果集合中剔除。
  7. 如果该竞价请求属于视频类型,则需要比较视频属性、播放时长等参数,不符合条件的从结果集合中剔除。

5.3.5、精普分离

        精普分离是将经过维度计算和参数计算后的广告执行单临时结果集合,遍历其中广告执行单的绑定热键字段。如果绑定的关键字列表和关键网站列表中有任意一个,且对热键有效期进行了有效赋值,则认为该广告执行单为精准广告,保存到精准广告集合中,否则保存到普通投放集合中。

5.3.6、精普流程

        精普流程详细描述了精准广告和普投广告的筛选流程。经过这些处理,即可完成最终的广告选单过程,上图左侧为精准投放的处理流程,上图右上图为普通投放的处理流程,上图右下图为用户属性查询操作的处理流程,具体描述信息见下文。

5.3.7、精准投放

        当精准广告集合中非空的情况下,调用精准投放筛选流程。用户的UA需要进行与iCache系统一致性编码,使处理后的值在iCache系统中可以有效定位到唯一用户。用户属性查询操作本着先本地缓存后远程iCache系统的顺序原则执行。用户属性集合为空则终止精准投放流程,直接跳转到普通投放流程。

        用户属性集合中包含关键字和关键网站两类,在集合中按照时间顺序排序,距离当前时间越近,排序越靠前。热键的匹配采用遍历的方式执行,在AC树中查找,命中则将对应的广告投放单ID集合提取出来,与精准投放集合执行多路归并算法,取交集。

        求交集合遍历过程解决两个问题,一方面将CPM和CPC广告区分出来,另一方面查找该广告位的CTR。CTR查找首先采用广告位ID和物料ID作为主键,查询失败改用广告位ID单独作为主键,再次失败则采用物料ID单独作为主键,依旧失败最终采用默认CTR作为估值计算标准。

  1. 对用户UA进行编码,保持与iCache系统的一致性。
  2. 如果用户属性执行本地缓存查找优先则实施查询操作。
  3. 否则对远程iCache系统执行用户属性查询操作。
  4. 遍历热键集合,从中提取一个热键在AC树中进行查询。
  5. 命中集合与精准集合执行多路归并算法求交集。
  6. CPC和CPM集合分离,CPC同时查询CTR,并按降序排列。
  7. 广告出价操作,失败继续热键集合的遍历操作,成功跳出返回。

5.3.8、普通投放

        当精准广告投放失败的情况下,执行普通投放策略。普通投放流程将待选广告执行单分别保存到CPC和CPM两个集合中,CPC查询CTR步骤与精准投放流程中的查询步骤一致,并按降序排序,CPM按优先级模拟排序。CPC相同时和CPM执行相同的随机选单操作。

  1. 创建CPC和CPM广告集合。
  2. 获取一个广告执行单,判断是否是CPC广告。
  3. CPC广告采用广告位ID加物料ID作为主键进行查询。
  4. 失败采用广告位ID单独作为主键进行查询。
  5. 再次失败采用物料ID单独作为主键进行查询。
  6. 依旧失败采用默认值作为CTR,并保存到CPC集合中。
  7. CPM广告按照优先级保存到CPM集合中。
  8. CTR相同和CPM一样执行随机选单流程。

5.3.9、用户属性

        用户属性查询是精准广告执行单选单的重要依据,用户属性查询有两种途径,一种是本地缓存方式,另一种是远程iCache查询方式,途径选择在程序启动入口参数指定。对于即搜即投产品仅适用于第二种远程iCache查询方式。

        查询操作分为若干个步骤,首先确定查询设备(本地缓存无此步骤),采用第三方平台ID和用户ID作为索引执行一致性哈希操作,返回设备信息;接下来连接设备(或本地缓存),发送并接收数据;最后将数据格式化到容器中,完成用户属性的全部操作过程。

  1. 一致性哈希获取用户属性存放设备信息(仅适用于远程iCache系统)。
  2. 初始化套接字并设置超时时间。
  3. 封装查询报文并发送。
  4. 接收报文,且当数据不为空的时候初始化容器用于存放用户属性数据。
  5. 解析用户数据到容器中。
  6. 关闭套接字。

5.3.10、本地缓存

        本地缓存是竞价引擎获取用户属性的一个可配置选项,在系统启动的时候通过参数指定。如果启动该选项,当本地查询成功以后,则不再向远程iCache系统发起查询请求,这可以有效降低iCache系统的负载。

        本地缓存查询服务遵循内部通信协议,在缓存中的存储同样也保持该格式,减少转换过程中不必要的计算消耗。本地缓存系统的设计与实现另见文档。

        本地缓存操作首先在布隆结构中对用户行为是否存在进行定位,只有确认存在的时候才会向本地缓存系统发起查询操作,否则直接返回失败,由竞价引擎向iCache系统发起进一步查询操作。

5.3.11、归并算法

        根据用户属性(关键字或关键网站)在AC树中查询成功将返回一个广告执行单ID的集合,这个集合与经过维度计算和参数计算后的精准投放集合存在差异,归并算法需要对这两个集合进行集合求交计算,以便得到满足条件的广告候选执行单。

        两个集合均是有序集合是多路归并算法的前提,且操作行为直接作用在精准集合中。首先从两个集合中各自提取出一个执行单,对执行单ID求差。首先判断是否等于零,是则表示命中,比较时间权重。否则判断是否大于零,是则热键集合前进至下一个执行单,否则从热键集合中删除该执行单,同时精准集合前进至下一个执行单。所有归并操作结束以后,将精准集合中剩余的广告执行单全部清除。

5.3.12、匹配算法

        匹配算法是对倒排索引进行查询,倒排索引的存储结构有键值两部分组成,键来自于广告执行单的字段,也是计算维度,值是若干广告执行单在广告配置内存中的存放地址的集合。匹配算法是对树进行查询操作,时间复杂度基本等于二分法查找。查询结果保存到临时结果集中,保存的同时完成排重操作,保证执行单在集合中的唯一性。

5.4、数据结构

        竞价引擎中的数据存储采用了内存容器,容器建立在共享内存和堆上,分别用于进程间共享和进程内独享。保存在共享内存中的数据采用集合容器,保存了广告配置信息、地域信息、CTR估值信息、用户频次信息、用户热键信息、广告位频次信息、广告投放信息等。保存在堆上的数据采用树容器,包括设备摘除索引、时间索引、平台索引、广告位索引、区域索引、移动索引、设备索引、连接索引等。

5.4.1、集合容器

        集合容器分两种,单纯的数据池以及包含数据池与溢出池的数据池。红色边框部分为单纯的数据池,绿色边框部分为溢出池。

        广告配置信息采用单纯数据池,数据在池中有序排列,初始化的时候从XML配置文件中解析各字段到数据结构中,全部数据导入完毕后按照广告执行单ID进行升序排序操作,采用二分法查找定位。

        地域信息采用单纯数据池存储,但数据间关系采用树的节点关联,通过父节点指向子节点偏移位置来定位。IP地址和地域信息描述从配置文件中读取并加载。

        CTR估值信息采用单纯数据池存储,每个节点包含广告位ID、物料ID、展示数、点击数和CTR估值。以广告位ID和物料ID作为主键,空值采用“Null”填充,主键作为排序依据,采用二分法查找。

        频次控制采用单纯数据池存储,以平台ID、用户ID和广告执行单ID作为主键,采用MD5对容器尺寸进行求模操作,忽略数据冲突情景。

        关键字(关键网站)采用单纯数据池存储,数据在容器中无序排列,仅作为AC树的镜像存在,并接收系统维护性查找操作。

        广告位频次控制采用单纯数据池存储,以广告执行单ID和广告位ID作为主键,采用MD5对容器尺寸进行求模操作,忽略数据冲突情景。

        广告投放信息采用单纯数据池存储,以广告执行单ID作为主键在容器中有序存储,按照二分法定位查找。

5.4.2、映照容器

        映照容器采用红黑树作为容器保存数据,存储空间在堆上,伴随进程存在。作为主键的索引不能重复,有效保证了数据的唯一性。

        在本系统中,映照容器用于存储倒排索引,以广告配置的可枚举字段作为主键,以广告执行单ID集合或广告配置信息各个执行单的地址集合作为值。

        摘除索引以广告敏感类别、广告类别、创意类别作为主键,广告配置信息的地址集合作为值。当竞价请求中包含上述三个类别字段的时候,该广告执行单将从待选集合中剔除。

        时间索引以一天中的二十四个小时作为主键,广告配置信息的地址集合作为值,每个小时对应该时段内所有可投放的广告执行单。

        平台索引以每个广告所投放的平台作为主键,广告配置信息的地址集合作为值,即该平台下所有加载配置的广告执行单。

        广告位索引以广告位作为维度创建索引,广告配置信息的地址集合作为值,即绑定了该广告位的所有广告执行单。

        地域索引以每个地域信息作为主键创建索引,广告配置信息的地址集合作为值,即当前所有绑定该广告位的所有广告执行单。

        移动索引以广告投放的移动或固网作为索引,广告配置信息的地址集合作为值,即每个广告在固网或移动网络上投放。

        设备索引以广告物料投放的终端设备创建索引,广告配置信息的地址集合作为值,即该广告展示终端为何种类型。

        连接索引以移动网络的连接方式作为索引,广告配置信息的地址集合作为值,即该广告将以何种方式连接展示。

5.5、类间关系

(略)

5.6、辅助机制

        竞价引擎是一个多并发多线程高响应的实时处理系统,为了确保监控线程与工作线程、以及工作线程内部之间的协调与同步,系统设计与实现需要充分考虑相关因素。

5.6.1、线程读写锁

        系统设计了一个全局线程读写锁作为同步机制的保障。监控线程在加载更新配置的时候对操作施加阻塞模式的写锁,工作线程在竞价过程中读取中间数据的时候,则对操作施加非阻塞模式的读锁,加锁失败立即返回,不参与竞价。

5.6.2、全局套接字

        主监听套接字以全局变量的方式存储在程序静态数据区,被所有工作线程共享,套接字默认阻塞模式,所有工作线程在报文读取调用的地方挂起等待,平等分享机制由系统内核保障。

5.6.3、一致性哈希

        iCache系统的地址信息通过一致性哈希获取,系统启动时初始化,将集群设备的地址全部加载,根据用户ID及UA等信息一致性出目标地址,经过与该设备的通信获取用户行为数据。

5.6.4、全局配置

        系统启动时读取的配置文件,以全局变量的方式存储在程序静态数据区,程序运行的过程过,以只读的方式读取,从而确保配置信息在整个进程生存期内有效并保持一致。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

厉力文武

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

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

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

打赏作者

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

抵扣说明:

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

余额充值