微服务回归单体后,代码减少75%,性能提升1300%……
旺道朋友圈广告 -> 最新发布


一、项目背景


内容架构是 QQ 浏览器搜索的内容接入和计算层,主要负责腾讯域内的内容接入和处理,当前接入了多个合作方的上千类内容。正如前面《如何避免旧代码成包袱?5步教你接手别人的系统》中提到,这是一套包含 93 个小服务的微服务架构。经过 23 年 Q1 的大力治理,让我们稳住阵脚,进一步对老系统做深入的评估:


  • 研发效率较低:新增一类数据需要在 3~4 个服务上做开发,代码量不多,但很繁琐。
  • 系统性能较差:数据流经多个小服务,且服务内部的实现普遍较差。譬如:核心服务的 CPU 最高只能用到 40%、一条消息从进入到流出需要经过 20 多次的反复 JSON 解析、多处存在多余的字符串拷贝和查找……


从架构和代码层面,我们看到系统存在较多的缺陷,同时我们也多次收到业务同学、上层领导对吞吐性能的投诉反馈,譬如:传输 6 亿的文档需要 12 天,太慢了;内容接入周期太长,成了某项目的瓶颈等等。


作为偏后方的基础架构系统,可靠高效是基本要求, 我们决定对系统做彻底的改造,设计简单的系统、写清晰的代码,提升系统性能和研发效率,为搜索业务提供稳定高效服务。


二、整体设计


内容架构主要做内容的接入和计算,支持的内容类型非常多,由于旧系统过度微服务化,且缺乏插件复用设计,使得需求开发人力较高,同时也存在性能缺陷、容灾不足等严重架构缺陷。新系统基于“零基思考”,重新规划设计,架构层面聚焦下面 5 个点:


  • 微服务和单体服务:旧系统由多个细碎的小服务组成,RPC 交互消耗很大,结合“处理量大、计算量小、失败容忍度低”的业务场景,新系统采用单体服务设计,数据在内存间流动,减少消耗。
  • 插件系统:面对繁杂多样的处理流程,旧系统没有插件化设计,代码里全是“if-else”逻辑;新系统我们使用插件化的设计,灵活支持业务需求。
  • 兼顾增量和批量(刷库):老系统应对批量数据处理(刷库)流程非常乏力,没有做流程拆分,使得刷库性能较差;新系统可以为刷库场景做定制化配置,大幅度提升刷库性能。
  • 故障容灾:旧架构几乎没有考虑容器迁移时的数据保障,新架构结合消息中间件实现流量削峰和消息缓存,实现故障时数据不丢。
  • 水平扩容:老系统的消费和计算没有分离,使得 CPU 最高只能用到 40%,且无法水平扩容;新系统将消费线程与处理线程分离,大幅提升单机处理性能,也能水平扩容。


旧系统设计:


文章图片1


新系统设计:


文章图片2


三、详细设计


1.从微服务到单体服务


十多年来微服务在后台系统大行其道,我们接手的老系统也是微服务设计,那么我们要继续微服务吗?


首先来看我们业务的特点:


  • 处理量大:每天有几十亿次内容新增/更新。
  • 计算量小:内容架构主要做接入和计算调度,计算量主要在下游的算子服务或者工厂。
  • 失败容忍度低:内容丢失便无法被搜索到,不能容忍内容丢失。
  • 内容类别多:已有上千种类型,还在持续增加。
  • 需求小且类别单一:所有的需求都是新内容源接入,需求类型较固定。


再来看老系统的设计,以接入系统为例,内网推送、公网推送、HTTP/Kafka 拉取这四类接入的实现,分散在四个服务上,再经过统一接入代理服务、数据处理服务、分发服务处理,一个条内容数据需 6 次 RPC 交互。在实践中带来这些问题:


  • 需要更复杂的容错处理:首先微服务群需要考虑超时时间合理分配;然后每一个微服务都需要考虑失败重试、重试雪崩等容错处理,复杂度随微服务个数成倍数增长。几十亿文档处理叠加上多个微服务,稍有不慎就会导致海量告警轰炸,甚至出现数据丢失。
  • 需求迭代慢:一个需求一般由一个人承接,需要改动多个微服务,整体代码量不多,但分散在多个服务中。
  • 计算浪费:内容数据在多个服务中流动,需要反复地做序列化和反序列化,而服务本身有价值的处理主要是字段转换、简单字符串处理等轻量计算,框架带来的计算消耗比本职计算还高。


最后,我们的新架构采用单体服务设计,在容错处理、迭代效率、计算量等方面都取得不错的效果(见文末数据指标)。


文章图片3

内容接入系统新老架构对比图


2.接入处理流程插件化


内容接入系统需要处理上千类内容,不同的内容通常来自不同的团队,各个团队都有一套对外输出内容的标准协议,因此内容接入系统需要编写大量的对接适配代码,如何更轻便地实现新内容接入,是我们重点关注的。


如设计图所示,我们的业务功能整体分为三层:接入层,处理层,分发层。


在接入层,我们需要处理多种途径接入的多种数据格式。途径包括:DB 定时拉取、Kafka 流式拉取、HTTP/COS 拉取、RPC 拉取等;数据格式也多种多样,每个数据方提供的数据格式各不相同。以 Kafka 拉取类接入为例,小说业务推送的是 JSON 格式数据,而小程序业务推送的是 PB 序列化的二进制字节流。


在处理层,不同的业务我们要执行不同的格式校验;有的业务收到数据后,需要再请求其他服务以补全特定属性;有的业务需要我们执行一些字段格式转换;有的业务需要我们对数据中的值进行定制化修改。


在分发层,每个业务要分发的目的地也不同:有的业务只需发往 Kafka,有的业务需要存入 DB、 Redis、DCache 等,有的业务需发送 HTTP / RPC 请求至特定服务通知更新。其中,Kafka 的 Topic、 DB 的存储表、目标服务的地址、协议也各有不同。


面对这样复杂的业务功能,老系统建设了一套数据处理流程,然后在主流程中通过 if-else 判断来走不同的处理流程,可以明显看到“堆代码”的痕迹,其源码组织的清晰度、功能的可插拔性都较差。


在新的接入系统中,我们将接入、处理、分发中的各个关键功能点实现为插件架构,每一个子功能都是一个插件,同时按照业务粒度的处理流配置组合使用插件。


例:批式接入任务执行流程


文章图片4


例:文档处理流程


文章图片5


当有新增的定制化业务需求时,我们只需要在相关环节增加插件,开发插件时,只需实现关键函数,如拉取任务插件只需实现拉取和拉取任务是否结束这两个接口。分发插件只需要实现分发逻辑;其余部分在框架层实现并统一调度,开发者无需了解。如果新业务只用到现有的功能,我们则只需要在 DB 中配置插件组合序列,无需代码开发。


通过此插件化设计,让业务接入更轻便,大幅降低业务需求的 LeadTime(见文末数据指标)。另外,老系统在各服务代码中各种硬编码 if 业务 ID == 指定 ID,则执行/不执行指定逻辑,排查业务问题时需要跨多个服务看代码,效率极低。而新系统只看配置便可清楚了解一个业务的接入处理全流程执行过程,极大地提升了运维排查效率。


3.兼顾增量更新和批量刷库


接入系统经常收到“刷库”类的需求:将指定业务的全部数据经过某个处理后发给某个指定下游。因老系统没有插件化设计,在组件组合使用上缺乏弹性,使得刷库需求不得不通过增量更新流程满足,因而做了大量无效计算。


新系统兼顾增量更新和批量刷库。我们结合接入系统的输入特点,将数据流配置分为了四种:数据源更新处理流、特征更新处理流、数据源刷库处理流和特征刷库处理流。


在数据源/特征更新的处理流中,我们需要配置业务线上数据处理的各类算子及分发算子。而在刷库处理流中,数据来源于我们的底表 HBase ,实际未发生变更,不需要重新计算。并且,在常见的刷库场景中,一个业务数据正常更新时需要分发给多个下游,刷库时只有部分下游需要重刷,此时我们只需要配置目标地的分发算子即可。


文章图片6


通过区分四类处理场景的数据处理配置,同一个业务在正常处理时和刷库时,新接入系统可执行不同的数据处理流,进而移除了刷库场景下的不必要计算和分发逻辑,单核刷库 QPS 提升了 16 倍。


4.数据接入服务故障容灾


数据不丢是内容架构的核心指标,无论数据是怎么来的,只要进入了我们系统,就应该保证不丢失。


接入系统的各类接入方式可归为三类:接口推送类、Kafka 通道类和定时任务批式拉取类。这三类接入方式中,Kafka 通道类自带数据备份,数据未处理完时不执行 Offset Commit,即可保证该数据不会丢失;批式定时拉取类的任务是可重入的,若拉取任务运行过程中进程退出,新节点重启任务即可恢复,数据不会丢失;只有接口推送类的数据可能在进程退出时未处理完,导致丢数据。


老系统对接口推送类数据没有做任何的保护,也就意味着进程异常退出、容器故障迁移等接入服务故障场景没有有效处理,数据可能丢失。


我们在新架构上增加了消息中间件 Kafka 实现数据容灾。对于 HTTP / trpc 接口推送进来的更新数据,接口层直接将其发进 Kafka,并返回给业务成功。此中间 Kafka 由指定的分区 (set) 进行异步消费处理,消息处理完成后才会执行 Offset Commit。


如在消费处理过程中,部分节点进程崩溃/退出,其他健康节点会通过接手消费处理对应分区的文档消息,最大限度保证数据不会丢失,同时消息中间件也带来削峰的效果。


文章图片7


5.消费与处理线程分离


老接入系统处理性能较差的重要原因在于:未将 Kafka 消费和文档处理线程分离。某业务配置 N 个线程处理,则这些线程先从 Kafka 拉取文档,再按照配置执行各环节的处理,处理完一批消息再去 Kafka 拉取,消费线程同时是处理线程,重计算的业务无法充分利用 CPU。同时,一个 Kafka 分区最多只能被一个线程消费,集群最大处理并发数受限于 Kafka 总分区数,无法实现水平扩容。


文章图片8


新系统设计了一个基于无锁队列的文档计算工作线程池,每个 Kafka 分区可以被一个线程消费,并被多个计算线程处理。通过消费和计算线程分离,充分利用 CPU,大幅提高了 CPU 利用率和处理性能。同时,计算线程数量不再局限于 Kafka 总分区数量,可以水平扩容。


文章图片9


四、新老系统diff校验


整个系统有 15 种分发出口,这些出口分散在老系统的多个服务。如果基于机器本地日志去比较 diff,显然零散且费力。因此,我们搭建了一个 diff 校验服务。


同时,在多个服务的分发出口进行埋点,并上报分发内容至 diff 校验服务,从而对分散的 diff 日志进行统一收集并分析。整个数据流如下所示:


文章图片10


比较 diff 的过程中,我们发现分发数据格式复杂,存在多种类型。例如,分发数据 Json Member Value 为一个 JSON 字符串,而 JSON 字符串 Member 的顺序是不固定的。为解决该问题,我们实现了一个递归的 JSON 对比工具,来校验多种类型数据的 diff。


五、编码细节


1.更少的代码


表驱动编程。如下图所示,重构后使用数据遍历替代冗长的 if 判断。


文章图片11


针对数据动态加载,使用 C++20 的std::atomic>替代原来双 buffer 设计,如下图所示。


文章图片12


2.更高的性能


用迭代器代替查找和括号取值。RapidJSON 的查找和中括号取值都需要遍历 member list,对于先查找后中括号取值的场景,可以先保存查找 member 获得的迭代器,然后通过迭代器来获取 member value,减少一次 member list 的遍历。


文章图片13



减少 JSON 反序列化。老代码的函数参数是 JSON 序列化后的 string, JSON 对象需要反复的反序列化和序列化,存在性能浪费。我们重构后,将需要多轮处理的 JSON 数据定义成 rapidjson::Document 对象并置于上下文中,消除了反复的序列化和反序列化。这不仅能提升数据处理的性能,还能减少重复的解析 JSON 代码片段。


文章图片14


3.更好的基础库


修复 rapidjson::Document 引发的内存泄漏假象,降低内存使用。为了减少重复解析,我们在 DB 拉取模块拉取到字符串后,就将其解析为 rapidjson::Document,然后存起来。


文章图片15


然而,执行上述优化后,我们发现 DB 每加载一轮,容器的内存就会显著上涨一截,加载 5-6 轮后,进程内存用满,发生 OOM。经过 Valgrind 工具分析和本地多种测试,我们确定实际内存未泄露,内存不断上涨是因为:使用 RapidJSON 基于内存池 MemoryPoolAllocator 分配器构造 Document 对象,在对象释放后,空闲内存不会立刻归还给操作系统。


系统分析后发现这和 RapidJSON 没有关系,是操作系统的内存策略设计如此。对此类内存释放不及时的问题,我们调研发现有两种解决方案:


  • 在服务启动时用 mallocopt(M_TRIM_THRESHOLD) 调低内存释放阈值,并在对象释放后,调用 malloc_trim(0) 强制其释放内存;
  • 通过过引入 jemalloc 等内存分配器。本项目采用链接 jemalloc 库解决。



此外,我们还引入开源的 Sonic-JSON 库。基于我们内容数据的评测,Sonic-JSON 比 RapidJSON 快 40%,因此我们引入了 Sonic-JSON 代替 RapidJSON ,在新接入系统的压测中显示,Sonic-JSON 可以提升 15% 的吞吐,或者降低 17% 的 CPU 开销。


4.更好的可读性


函数遵循单一职责原则。如下图所示,针对不同的订阅类型,老代码中职责不清晰,在函数中通过 if 判断来使得不同的订阅类型走不同的特殊处理逻辑。重构后,我们使用多态设计,不同的订阅类型派生类继承基础类,并针对自己的特殊逻辑进行泛化,从而使得每一个类只处理一种订阅类型。


文章图片16


将 switch-case 转换为工厂。如下图所示,应用插件设计和查表法,提高代码的可维护性和扩展性。


文章图片17


插件化和配置化。功能组件可以自由组合,从而避免频繁出现 trick 代码。如下图所示,在老代码中,通过硬编码实现对指定资源类型做指定的处理。重构后,不同资源可配置不同的处理流程,实现功能热插拔和组件复用。


文章图片18


六、研发流程


1.整体流程


研发流程上,我们沿用开发搜索中台技术产品时积累的 CICD 建设经验,包括以下措施:


  • 需求确认和启动,约定 TAPD 必填字段、TAPD 扭转流程。
  • 开发者资质,只有获得开发者资质认证,才能输出生产线代码。
  • 编码和注释规范,统一采用腾讯编码规范和 doxygen 注释。
  • 代码评审,制定可按步骤执行的流程,并提供学习案例。
  • 基础库规则,统一第三方库、工具库使用规范,消除项目依赖混乱。
  • 流水线,统一 MR 模板,严格约束静态代码质量检查红线、单测覆盖红线等。
  • 版本规范,统一版本命名和使用规范:MAJOR.MINOR.PATCH。
  • 发布流程,腾讯域采用 XAC 发布。


2.需求管理


在需求规划时,我们按大模块(或功能)划分大需求(EPIC),并把大需求分发给不同的开发人员。开发人员在梳理出模块的详细实现后,再自行划分出不同的小需求(feature),并调整对应的开发耗时。开发过程中使用甘特图,可以方便确定项目开发进度。


多人协作,难免会出现工作量分布不均匀或者需要延长工期,所以我们在每周三早上有一个十几分钟的晨会:确定需求进展,可能风险则及时调整开发人力,保障团队目标达成。


文章图片19


3.代码评审


代码质量对项目的长期发展有至关重要。我们团队要求每位开发者都必须通过代码安全考试和规范考试,生产线的每一行代码都需要经过 CR,同时鼓励全员提升代码品味,写出一手好代码。这里推荐一篇腾讯技术 Leader 总结的 Code Review 指南,非常有参考性:《腾讯 13 年,我所总结的 Code Review 终极大法》。


4.文档协同


文档可以跨越时间限制,是一种高效的异步沟通工具。在接手内容架构系统后,我们补充了大量文档,包括资源接入现状、系统链路、日常运维和各种排查文档,为稳定性维护提供了重要保障。


在系统重构过程中,我们也积累了各类文档,存放在小组各个方向目录中。同时在代码仓库里,一些复杂的业务逻辑或者复杂的模块,目录下维护着 README.md,说明模块功能、设计、实现和使用方法。


文章图片20


5.流水线加速


蓝盾流水线是实现 CICD (持续集成持续部署) 的核心工具,我们在代码发起 MR 后设置了MR流水线,代码合入主干后设置了主干构建流水线。


MR 流水线是代码开始 CR 前必须通过的红线,所以 MR 流水线的执行耗时会影响到整个 MR 耗时和需求开发耗时。针对重构期间多人协作出现大量并发检查任务,以及对流水线关键路径的耗时分析,我们做了如下优化。


1)减小流水线锁粒度


MR 流水线包含了代码安全扫描、代码规范扫描、单元测试、接口测试等多个步骤。接口测试需要共享特性环境作为部署和测试环境,存在资源竞争。之前限制整个流水线只能有一个构建在执行,其他都要等待。通过配置蓝盾流水线模板的互斥组,可以实现 stage 级别的锁,多个构建可以并行执行,仅接口测试 stage 互斥,使得流水线构建可以加快 25% 以上 。


文章图片21


2)使用 GitHub 镜像提速


我们有一个公共仓库专门存放各类外部依赖,通过 genrule 生成可被 bazel 直接导入的规则,外部依赖需要通过 tar 或者 git 获取源码数据。在实际执行过程中,发现部分外部依赖拉取异常缓慢,卡在 analyzing 步骤,甚至造成编译失败。


在分析 log 后发现部分含有二进制依赖的第三方库,直接从 GitHub 拉取会 QPS 出现卡顿,因此我们修改了 bazel genrule 的生成规则,全部使用镜像代理。实测中,发现部分任务卡顿会超过 3 分钟,优化后不再卡顿。


七、业务效果


1.性能收益


1)性能提升指标概览


内容接入系统:


文章图片22


内容计算系统:


文章图片23


2)处理性能 - 提升13倍


新系统单核性能从 13 QPS 提升到 172 QPS,处理性能提升了 13 倍。


以视频业务为例,旧接入系统处理峰值为 33465/min,总核数为 40 核,平均单核处理 QPS 为 13。


文章图片24


迁移到新接入系统后,处理峰值为 32119/min,总核数 6 核,平均单核处理 QPS 为 90。下图可以看到调大并发处理的线程数后,处理性能会等比例提升。当 CPU 压到 100% 时处理 QPS 峰值可达 162。


文章图片25


3)刷库性能 - 提升10倍


通过拆分增量数据更新、批量刷库的处理流,我们为刷库场景做定制化配置,大幅度提升刷库性能,集群刷库性能从 1000QPS 提升到 10000QPS(受限于外部存储性能),提升 10 倍。性能对比如下图所示:


文章图片26


4)处理延迟 - 降低70%+


平均处理延时从 2.7 秒降低到 0.8 秒。以视频业务为例,旧接入系统处理一条消息需要经过 5 个系统。每个子系统的性能又较差,p999 处理延迟达到十几秒。


文章图片27


新接入系统处理一条消息仅需经过 3 个,且系统性能较高,p999 处理延迟为秒级。


文章图片28


2.研发效率收益


1)研发效率提升指标概览


文章图片29


2)业务需求 P80 leadtime - 下降82%


得益于代码质量提升、单测覆盖率提升、微服务合并为单体服务、插件化的设计,在新接入系统下开发新功能或者业务定制化功能,开发难度和开发成本大幅下降,从 5.72 天降低到 1 天。


3)代码总行数 - 减少75%


重构后,业务代码量从 11.3 万行降低到 2.8 万行,下降 75%。主要由下面几点带来:


  • 微服务合并为单体服务。多个微服务小仓合并成大仓后,消除重复的功能代码。例如旧系统不同业务 Kafka 接入时,都拷贝了相同的一套实现。
  • 优雅的系统设计。譬如:插件化设计,消除大量的 if-else;序列化对象传参代替字符串传参,消除大量的 JSON 解析。
  • 现代 C++语法的大规模使用,让代码更精简,譬如:必要的 auto、for-range、emplace 等。


作者丨李浩津

来源丨公众号:腾讯云开发者(ID:QcloudCommunity)

dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn


关于我们

dbaplus社群是围绕Database、BigData、AIOps的企业级专业社群。资深大咖、技术干货,每天精品原创文章推送,每周线上技术分享,每月线下技术沙龙,每季度Gdevops&DAMS行业大会。

关注公众号【dbaplus社群】,获取更多原创技术文章和精选工具下载

教育培训机构-如何策划节日招生?
做教育培训,最重要的事情莫过于招生了。面对不同的学员,招生方…于招生。目标不同,活动策划也会不一样。② 活动主题要鲜明不仅主题要鲜明,主题之间要有连续性。就像看连续剧一样,一集接一集,一环扣一环。策划好一系列活动,一个接一个来,从多个角度延续活动的宣传,突出主题,引起关注度。常用的招生方式有:体验课招…-------------想要获得更多运营技巧、doc文案,
教育培训:如何提高满班率和续班率
什么是满班率?就是实际人数除以计划招生人数,这个值就是满班率…员有流失是正常的,但要保证一个概率。在保障老学员续期的基础上,我们也要大胆创新,开设网络课程,拓宽招生区域。实地课程录制后,进一步发行到商弈云课上,通过商弈云课系统转成真实流量,网上有人点击观看,点赞,可以按点播量分佣金。助你在日常的营销推…-----------------------想要获得更多运
培训机构难盈利的问题出在哪里,如何实现盈利?
竞争白热化实地培训班、托管辅导班竞争白热化了,利润越来越稀薄…现没有多少盈利。还有一部分负责人在刚招生时,掌握了不少资金,开始盲目扩张,盲目做课程推广,以期待获得更多生源和收费,从而使培训班陷入了这种恶性循环的陷阱里。其实这些到手的钱,并不是你的利润,而是你未来要支出的钱。如遇到行业快速发展时期,或者…-----想要获得更多运营技巧、doc文件,请“关注”并“转
“差异化”经营是网络培训赚钱的关键所在
“差异化经营”这个词,相信大多数人都耳熟能详,但要做到的却是…。在现有的基础上更新升级或者资源整合就是对现有的产品或者技术,进行升级版本,或者通过一些资源整合,成一个项目。你的技能以前是用来生产或制造产品,现在是把技能升级成知识培训,转换云课堂。直白一点就是可以让老师自行录好课程并上传到学校的商弈云课…在着缺点和不足之处。在创新改革时,要从功能、服务、价格等方面
教育培训行业是如何运作的?
以前读书时,学校附近有个小摊卖鸡蛋灌饼。刚开始的时候只有一家….%,剩下的一对一和多人大班其实市场份额都很小。家长报培训班,是希望孩子能够学到知识。老师通过课上教学和课后辅导来满足孩子的需求,达到成绩提高的目标。我见过上百人的大培训班,为什么一个班这么多人?因为是名师讲课,但人太多坐后面,基本听不到重…是你的核心竞争力。想要得到用户的认同,你就必须做到产品质量好
旺道:实体店怎么做网络营销?
年纪大了,体力不好了,便赚不了什么钱了。所以,一定要在年轻的…写文案都是非常头痛的事情,那么下面我会给大家提供一个解决方案。在互联网营销系统里面有很多细节营销,软文营销就是其中一种,但好多小白头痛写文章,所以可以用这个方法也一样可以植入广告语,或者是图片广告,轮显切换广告,使用 旺道营销系统,也就是每…以帮你吸引海量精准粉丝。有了粉丝,你通过广告、产品、服务就可
旺道:圈里圈外,铁饭碗距离瓷饭碗究竟有多远?
中国人爱混圈子,从线下一直混到线上,各种奇形怪状的圈子勾勒出…不能推则敷衍了事,盼着混到主任科员退休;一种是老黄牛式,踏踏实实、兢兢业业工作,但拙于人际关系,活干得最多,但依旧不见晋升迹象;还有一种是提线木偶式,你扯一下线他动一下,好像脑子和身体是分离的。我突然一拍脑袋,这是提前衰老的迹象啊,工作内容…即使选择留在铁饭碗圈里,也应保有随时离开的思想警惕和进行必要
旺道:微信要双开了,朋友圈推广更好做了
现在手机只能登一个微信,如两个,三个微信的小伙伴们就只能退出…动推广工具、 设置头像点击头像图标,点“+”选择图片,可以上传你的企业,个人头像,产品服务等。旺道微信朋友圈广告免费朋友圈广告-旺道、 设置推广主题点“广告”,再点“+”设置推广的主题(类别),填好中文主题,以及你的网址。教你不花一分钱做朋…简直就是一劳永逸哟。如果你点开发布的文章看不到广告,那就是你
旺道:如何能让网站多个关键词排名百度首页?
最近接了一个单,做婴儿游泳馆加盟的。优化推广范围是全国,但是…泳馆加盟哪家好”、“婴儿游泳加盟多少钱”这类与我息息相关的词。如何能让网站多个关键词排名百度首页?但客户并不认同,客户说这个词指数低,或者说搜的人不多。是,这是一方面原因。但是,指数低有什么关系,你是卖产品还是卖指数。搜的人不多有什么关系,…实现广告效果最大化,来满足大众化的广告需求。对于这样的四两拔
旺道:如何成为网络营销达人?这个技能帮你节省80%的时间
在某次名师讲坛上,听到了一个让人警醒的故事。说有个村子缺水,…准的产品。现在已经不是新鲜事物,好多企业因为百度竞价的高额费用,头疼不已,所以做网站关键词优化排名,成为了企业必选的推广项目。做优化也要做到有成效,不然就是一分钱也是昂贵的。用 旺道智能机器人 就可以持续代替人工去做,它非常聪明,只要你够耐…手,设备先进,你多努力奔跑也于事无补。简单说,要去一个地方,

此文章来源来互联网,如果你有任何版权冲突,可以联络我们,微信号:WANCOME,QQ:1444641。加我们时,请说明来意,我们将优先处理你的问题。




商弈云推  | 一键推  | 随手推  | 圈推广  | 跨圈推
粤ICP备10213132号
旺道商标注册证号:8608864
Copyright © 2016 环企网络信息科技有限公司 版权所有