关于我们 | 联系我们

亚慱体育APP - 正版APP下载

当前位置:主页 > 产品展示 > 产品一类 >

详解腾讯广告系统大规模C++工程实践‘亚慱体育app官方下载’

本文摘要:作者:陈峰 广告工程效能卖力人、专家工程师 ,高路 广告工程效能高级工程师引言大家好,我叫陈峰,来自广告,卖力工程效能事情,这位是我的同事高路,以下是我们的简介。今天我们要分享的内容是《广告系统大规模C++工程实践》,广告是由公司推出的广告系统。是现在公司最重要的业务之一,依托于海量优质流量资源,给广告主提供跨平台、跨终端的网络推广方案,并使用大数据处置惩罚算法实现成本可控、效果客观、智能投放的互联网效果广告平台。

亚搏手机版app下载苹果

作者:陈峰 广告工程效能卖力人、专家工程师 ,高路 广告工程效能高级工程师引言大家好,我叫陈峰,来自广告,卖力工程效能事情,这位是我的同事高路,以下是我们的简介。今天我们要分享的内容是《广告系统大规模C++工程实践》,广告是由公司推出的广告系统。是现在公司最重要的业务之一,依托于海量优质流量资源,给广告主提供跨平台、跨终端的网络推广方案,并使用大数据处置惩罚算法实现成本可控、效果客观、智能投放的互联网效果广告平台。

这是广告系统的基本架构,是一个很典型的漫衍式互联网服务系统。其中在线部门的广告引擎、算法计谋以及计费平台占据了整个系统的大部门资源开销。几万台机械,天天要处置惩罚数百亿次请求,因此这部门对性能的要求很高,是以C++为主实现的。其余部门的除了C++外另有java、go、scala等其他编程语言,由于跟今天主题关系不大,在此不表。

我们现在整个系统有约1000名工程师,十来万个源文件,上千万行源代码,算得上是一个大规模的系统了。在这种规模下,我们保证了代码的开发效率和质量,一些焦点模块做到了天天一次的公布频率,到达了业界较为领先的水平。

下面我们就从这几个方面为大家论述最近几年的工程实践。1、代码库治理首先我们开始第一个话题,代码库治理。规范而科学的代码库治理对保证开发效率和质量很是重要。

我(陈峰)2013年加入广告团队,那时候业务突飞猛进,可是代码库缺乏治理,好比组织杂乱、气势派头纷歧、存在大量重复的库、缺乏测试、难以完整乐成构建等。这种情况适应不了项目的大规模生长,必须举行革新。首先要举行代码规范和代码库组织的整改,我们选择了盛行的Google代码规范。由于厥后制定的C++代码规范也是基于Google规范,因此只做了少量修改就自动适配到了公司规范。

不外只有代码规范是不够的,所以我们还制定了代码组织规范。只有规范显然还是不够的,因此我们还通过一系列的流程和工具来确保这些规范获得有效地实施。先来看一下代码库组织和开发模式,我们接纳的是单一代码库主干开发模式。

这种模式是Google公司最早实施的,而且在获得了许多其他公司的效仿,好比Facebook、Microsoft、Twitter等等。这种模式是现在技术能力雄厚的大规模互联网公司的探索到的效率最高的工程实践,有许多利益,参见列表。固然,这种模式也是一把双刃剑,除了这些利益外,也有不少挑战,后面我会再仔细讲述。

在单一代码库下要想治理好代码,首先需要划分好项目,明确每个项目的责任人,并落实到代码库的差别目录中。代码公有和开放并不意味着可以随意改动代码,任何代码修改都需要经由CodeReview才气合并到代码库中,我们通过OWNERS机制决议谁来Review和批准相应目录下的变换。

现在业界有两种OWNERS模式,一种是右侧这种github支持的集中式方案(在代码库的特定路径下放一个CODEOWNERS文件),另一种是左侧我们这种疏散式方案。在大代码库下,很难做到集中式控制,基于目录级此外OWNERS文件的疏散式权限控制更合适,我们这里是借鉴的Chromium项目的OWNERS文件设计。可是对其语法做了一些扩展,以适应我们的治理模式。好比通过逻辑表达式来支持跨组协作评审一些重要的协议界说,而CC机制则让还没有获得评审批准权的组内新人也能收到通知,努力到场代码评审。

1.1、构建系统构建系统的统一也很重要。这部门原来算是工具,不外因为它和代码库的组织密不行分,因此我们放在这里提前先容。

Blade是我们为单一代码库模式开发的多语言构建工具,2012年对外开源。2013年广告系统改用Blade构建,解决了先前基于GNU Make构建的大量问题。

Blade让整个代码库的依赖关系成为一个完整的DAG,为准确有效地增量构建和增量测试打下了基础。内置的测试支持为提交前的检查和自动接入连续集成打下了基础。固然Blade只是我们在谁人时候的选择。

现在也有许多其他类似的现代构建系统也不错,关键是要支持对代码库的整体依赖治理、增量构建以及自动运行测试。1.2、代码组织通过统一构建系统,我们还可以做到一系列的对代码组织方式的进一步改善。好比,传统上C++和C语言头文件包罗时不带路径。

可是这种用法不适合大规模项目。因为,差别目录下的头文件很容易重名,导致解决甚至很难发现的构建甚至运行错误。我们通过要求头文件都用完整路径来包罗来解决这个问题,差别种别的头文件之间还可以分组,代码看起来很清晰,阅读和浏览都很利便。

通过构建系统还可以控制头文件的可见性,区分出库的公有头文件和私有头文件,让库的对外接口稳定可控。虽然无分外依赖不需要单独编译的全头文件的库也广为盛行,可是这种库的实现方式加大了耦合,降低了代码复用和编译速度,并不适合大项目。我们不勉励在自己的代码库中这么做。

1.3、命名空间在大规模C++项目中,命名空间也很重要,可以制止潜在的名字冲突。我们要求了列表中划定的规则,命名空间的名字基于项目名是因为组织架构的调整的频率往往比项目变更要高得多,好比这里的 gdt 是早期的产物代号,到现在为止不算太过时,如果是部门的名字,早就和实际情况纷歧致了。差别于Java等语言的“包”的观点,C++命名空间主要用于制止意外的名字冲突,因此我们不提倡每一级子目录都有其对应的命名空间的做法。

因为using namespace会造成命名空间污染,对于跨项目的符号都是带着命名空间前缀的方式使用,此时如果命名空间很深,代码会显得烦琐冗长。一般命名空间到项目一级就足够了。同一个项目内部会相互Review代码,也经常会一起链接,命名冲突比力容易发现和协调。1.4、依赖治理除了头文件外,库也一律通过全路径来引用,解决了差别目录下的库文件可能重名的问题。

通过构建系统,还能约束库的使用规模。好比我们的代码规范中要求限制boost的使用,而且我们发现现实中许多对C++生长不熟悉的法式员对boost的使用都是诸如function、shared_ptr等已经纳入C++尺度的组件,这个约束可以让项目引入boost时至少有个可控的评审历程。这个功效也适用于业务项目中的库,制止项目内部自己开发的内部库在不知情的情况下被此外模块依赖。

1.5、第三方库治理现代化的软件开发离不开大量的第三方库,包罗开源库。第三方库提高了开发效率,可是也带来了一些问题,好比:代码气势派头纷歧致:开源代码的代码气势派头纷歧定和我们一致,C气势派头的库要封装为C++接口适应现代C++气势派头;兼容性问题:同一个库差别版本的代码混在一起,很容易导致种种很难明决甚至难以发现的问题,浪费大量的开发和调试时间。另有种种宁静毛病和License等问题。

因此,我们制定了第三方库的治理规范,对第三方库举行统一治理。1.6、基础库建设众所周知,C++是一门基础库十分单薄的语言,因此我们又在第三方库的基础上,建设了大量的基础库,并在基础库的基础上建设了服务开发框架,降低了业务代码开发的难度。我们勉励业务开发人员对于开发历程中遇到的任何某个通用功效,优先思量看基础库中是否已经存在,如果不存在就到场孝敬。

几年下来,这部门的代码就扩大到了几百个模块,16万行代码,大大利便了业务代码的开发。2、效能平台与工具固然业务代码的增长更快。大量的代码放在一起,要治理起来不是一件容易的事。

需要代码工具平台的强力支持。这是我们现在的现状,可以看出,如果没有好的工具支持,效率和质量很难保证。

我们用右面这些应对机制来解决这些问题。有许多机制已经成为业界公认的优良实践,我们就不再详细先容,下面重点讲一些特有的工具。2.1、特性开关大量的代码在一个代码库的主干上开发虽然大幅度降低了恒久分支开发模式下的合并冲突,可是可能会导致不成熟的代码被公布到线上,我们开发了特性开关治理系统来治理开发中的特性。为了制止代码中存在大量的已经上线固化的开关,自动跟踪每个开关的声明周期,驱动业务开发人员清理逾期开关及相应的代码。

可以到https://featureflags.io/ 网站上详细相识此项技术。2.2、代码构建履历我们再分享一些构建方面的履历:首先,只管接纳从源代码构建,不用二进制库。制止了种种二进制兼容问题,也利便编译器升级。

最近看到一个相关的演讲(https://www.youtube.com/watch?v=54uVTkhinDE),和我们的做法不约而同,有兴趣的话可以深度相识一下。说到编译速度,许多人会想到预编译头文件。其实,和期望的相反,由于一系列的问题,预编译头文件对大规模C++项目的恒久康健是倒霉的,我们没有接纳。

我们未来会思量跟进C++20 Modules的希望。最后,可执行文件只管接纳静态链接,包罗libgcc和libstdc++,可以减轻部署的庞大性,淘汰对编译器升级的肩负。

2.3、保密代码构建系统前面说过,我们99.9%的代码都是全员可会见的,可是究竟有少少数敏感的,好比涉及焦点计谋和商业秘密的代码需要保密,只允许少数相关的人员会见。由于上述二进制库的问题,这些代码我们依然希望以源代码现场编译方式来使用。因此我们开发了敏感代码构建系统,需要保密代码放在单独的堆栈中,并在主代码库中通过BUILD文件来引用。构建时通过在对保密代码库有会见权限的服务器上远程编译,获得编译效果。

亚慱体育app官方下载

纵然保密代码里引用的主代码库中的头文件有当地修改,也能获得正确的效果。因后面的内容更多是高路在主导,因此接下理由他讲述。2.4、漫衍式构建系统今世码库很大的时候,纵然有了很强的依赖治理和当地缓存,单机的构建速度依然不足,因此我们开发了漫衍式构建系统。

我们调研了业界的一些产物,如distcc。distcc要求每台机械都需要知道集群中的其他机械,这对于大规模工业化场景而言使用是比力难题的。另外,distcc每台机械并不相识其他机械的负载,高负载时容易压垮某台编译机甚至因为耗尽内存死机。Suse基于distcc开发了具有中心化调理器的icecream,可是我们实际测试中发现类似于卡住没有进度之类的小问题比力多,调试了几天之后放弃了。

仔细学习了这些同类产物之后,我们设计了一套基于中心化调理的漫衍式构建系统yadcc。右边是我们的整体架构。我们通过中心化的调理器降低了接入成本,客户机只要知道调理器地址即可接入,同时调理器也制止了高负载下压垮某些机械。之后客户端通过当地的守护历程提交任务。

编译缓存方面,一般来说构建系统不会重复编译没有修悔改的文件,因此当地缓存我们认为收益并不大。可是我们广告这种多人基于一个大代码库的场景下,各个用户之间是可以共享缓存的。好比用户1编译后push,用户2pull下来的情况。

因此我们增加了漫衍式的编译缓存来促进这种共享。对于编译新代码的情况,为了制止不须要的缓存查询引入延迟,我们还在守护历程里增加了布隆过滤器。预处置惩罚、回落当地重试、链接等不行漫衍式的操作,我们也会通过客户端和守护历程协作,控制并发度制止当地机械过载。

在性能方面,我们注意到漫衍式编译性能瓶颈往往在于当地。因此我们通过-fdirectives-only加速预处置惩罚,某些特殊情况下这个参数预处置惩罚会失败,我们会静默重试普通的-E预处置惩罚。为了降低网络压力漫衍式编译通常会对数据压缩,我们选择了zstd,可以提供比gzip更低的CPU开销。

生成缓存key方面,我们发现sha256速度并不快,对预处置惩罚效果盘算sha256会增加约莫25%的cpu开销,因此改用blake3算法生成,cpu开销降低至5%。我们基于类P2P方式设计,每台加入集群的机械即是用户,也是服务器,制止人数增加导致集群过载。最后,调理器会将差别版本的编译器隔脱离,这有助于我们后续继续平滑升级编译器。这是我们在一台8核志强铂金虚拟机上的测试对比,llvm-project在漫衍式情况下只需要约莫3分半左右就能构建完成,当地构建则需要靠近50分钟。

3、现代C++实践下面我们再分享一些如何让代码库跟进到现代C++方面的履历。3.1、编译器升级要跟进C++尺度的演化,首先就需要实时升级编译器。每次升级都免不了需要做一些对代码改动,由于我们是单一代码库,所以升级编译器只需要让整个大堆栈可以正常构建即可,不用担忧升级了编译器破坏了某处我们不知道的代码的构建。

在升级历程中我们会对新版本的编译器也建立一个连续集成流水线和现存的一起跑,确保过渡期间不会有破坏新版编译器的代码变换。然后在一些重要服务上线验证后即可全量启用。迁移后我们再通过代码评审前的代码检查工具引导开发人员使用新特性。

右图是我们升级编译器的一些历史,在2018年尾,我们全面切换到了GCC8.2。现在来看GCC11把C++20实现的差不多了,后续我们可能思量升级到GCC11.2。

3.2、Flare服务开发框架除了更新编译器之外,基础库也需要跟上现代化C++的设计思路来获得越发统一高效的开发体验。因此,我们根据现代C++的理念,相应地设计和更新了我们的服务开发框架,代号Flare(耀斑),寄托着快速和耀眼的优美寓意。在基础库建设方面,我们广泛使用新版尺度新增的类型和库改善可读性及性能。对于部门编译器未支持,或尚未合入尺度,可是我们以为很好的功效或者提案,我们也会通过引入第三方库或自行仿制来引入。

C++的尺度库的功效现在相对较少,对于常用的功效,我们也根据现代C++的思想设计了相关的类库来补齐。最后,对于部门需要扩展性的地方,我们通过C++中常见的traits设计,提高了扩展的灵活性。这里以字符串剖析为例,上面的是我们以前基于C++03的接口设计,需要用户界说变量吸收输出并检查返回值,如果遗漏了检查可能会使用到无效值。

下面的则是我们基于optional的新的设计则,将二者合二为一,简化挪用方式,并能制止了错误被无意忽略。除此之外,基于traits的设计还允许我们利便地扩展支持新的类型,如这里针对Endpoint的特化,允许我们用统一的TryParse接口,来支持剖析ip-port。其他C++类型也可以通过类似方式扩展TryParse对其的支持。

另外,通过tag-dispatching机制,我们还可以区分ipv4/ipv6来剖析。Tag-dispatching也是尺度库常用的设计,如unique_lock接受的adopt_lock等。

除了基础库外,高质量的服务开发框架也是开发效率和运行效率的重要保障。多协议、多监控平台支持等是服务框架的基本要求,各个框架都市有相应支持,这儿不多赘述了。底层来说我们基于用户态线程实现,这样可以提供应用户一个同步的开发接口,同时通过用户态调理保证性能。

对于异步场景,或者并发异构的场景,我们通过自己实现的一套Future支持。我们这套Future的阻塞等候反面线程模型绑定,因此可以很好的和用户态线程联合。

接口方面我们大量使用了现代C++提供的类库,这可以很好的改善我们的开发效率。现在C++也有了原生的协程,现在主要是core-language的支持,后续随着我们的编译器升级,我们可能也会以Future作为前言,支持C++的协程。不外现在来看,C++协程不能完整解决后台开发的难题,好比加锁等需求,因此短期内我们期望协程在生产情况中如果使用,还是需要和fiber和谐共存、交互。

这是一个基于我们RPC框架开发的例子,演示了同时并发向两个地址的服务器提倡请求的历程。函数在一个Fiber中运行,超时部门使用C++14的chrono的UDL,可以直接写类似于1秒这种写法,制止写1000并注释说明单元为毫秒等“传统”写法。

Stub.Echo返回Future<Expected<…>>(C++提案中的错误处置惩罚方式),这样我们不需要分外的逻辑去划分接受返回值、错误码。挪用BlockingGet时当前Fiber会被刮起,完成后恢复,获得了一个rpc效果的tuple,再通过structured binding获得两个回应。

最后,如果rpc乐成,我们通过C++20的format气势派头,把效果输出到日志。经由种种针对性优化,新的RPC框架的性能和延迟都有很大的改善,特别是延迟平稳性的大幅度提高,对广告系统的平稳性和精准性都有较大的资助,获得了业务开发团队的好评。最后,我们再总结一下我们的履历,列在这里。由于篇幅有限,今天的分享就到这里。

大家有问题接待交流讨论。


本文关键词:详解,腾讯,广告,系统,大规模,C++,工程,实践,‘,亚慱体育app在线下载

本文来源:亚慱体育app在线下载-www.hengrongsh.com

Copyright © 2008-2021 www.hengrongsh.com. 亚慱体育app在线下载科技 版权所有 备案号:ICP备44991172号-8