云计算 频道

百度外卖营销技术实践

 

 

     大家晚上好,我是今天的主讲嘉宾,我们会在八点钟准时开始,欢迎大家多提问,本次直播不会禁言,欢迎大家多提问,我会在合理的时机统一给大家做回复、回答。

大家晚上好,现在就开始我们今天的直播,首先给大家做一个简单的自我介绍,我是2014年加入百度,目前在百度外卖是营销方面的负责人,我来给大家安利一个公众号,这个公众号是我们百度外卖的技术团队统一对外呈现的公众号,他上面是有非常多的我们对外的优秀的技术文章,也会有一些技术的公开的交流学习,欢迎大家关注,首先先给大家说声对不起,由于昨天晚上部门的篮球赛加油过于努力和用力方式不对,造成今天嗓子有点哑,还望大家见谅,下面我们就进行今天分享的主题。

对于架构这块其实有很多的东西可以讲,今天由于时间有限我主要是给大家讲四个方面,抽象、隔离、解耦和降级。

 

讲这四个小点之前我先简单的给大家介绍一下目前百度外卖的营销系统,具体负责哪些东西,图中所示,目前百度外卖的营销系统主要是由券,用户成长,营销工具,营销数据中心,功能较多跑腿和消息中心组成。

在我刚来百度外卖的时候遇到过所有的初创公司都会遇到的一个问题,如图中所示,由于我们初期系统快速的迭代的要求,所以我们有很多系统都是没有太考虑其它的问题,直接来一个需求就开发一个新的系统来满足需求。这种做法在初创期是非常行之有效的,可以确保我们的需求最快速的迭代,保证我们对业务发展的最大力度支撑,可是在业务成熟期,包括业务的后续的发展期可能会带来一定的问题。

百度外卖的营销系统也是在业务飞速发展的情况下,快速迭代出来的,我发现整个的营销系统是由若干个相对独立的,功能不是很多的小的系统功能点组成的,我们拿出其中两个小的功能点来看一下这些功能大概是什么样的,比如说第一个功能点是下单返券,针对一些特定的商户,我们可以在用户下单完成之后给用户发放一个代金券,在这个系统中有很多相关的功能,它自己有一个小的管理后台,有发放代金券的代码逻辑,也会有如何判断订单完成的代码逻辑,还有UI的外录,同样我们发现还有一个叫做下单返余额的小的功能,这个功能从名字上大家可以发现他是和下单反券非常相似的,是下单返余额,他存在了一定的区别也有很多的共同性,比如说他也有一个小的管理后台,他发放的不是代金券而是发放的余额,但是发放的逻辑相似,而且他也是在订单完成后进行发放的,同时他针对的群体不一样,之前下单反券是针对商户,下单返余额是针对的特定的用户群体,这两个功能点大家可以发现有非常多的相同和相似的地方,但是由于整个初期的快速迭代,所以整个的功能点相对来说都是独立的,代码也是分离的。

   这样的系统其实如果没有后来的问题也是可以正常的保留下去继续发展的,但是一般随着业务的细化和发展我们通常都会遇到很多的问题,比如说如图所写的,将给所有的工具加个统一的审核功能,当你需要增加一种新的营销工具,比如说我们未来会有一些其它的类券的营销资源或者是其它的小工具,包括当我们需要进行最优优惠的时候,我们会发现对现有的系统进行改造太痛苦了,我们就会想到我们是时候需要抽象了。

这个概念在很多地方都会有应用,作为程序员我们写代码的时候都一定会先过抽象这一关,因为我们有非常多的方法是需要做抽象的,只有抽象做好了,我们才可能会让我们的方法有更多的复用,同时可能才会减少我们后续因为改变带来的过多的重复性的开发,对于代码来说我们要做抽象相对比较简单,因为整体的可控性比较高,但是对于一个复杂的系统群我们怎么样做抽象?我们停留在口头上说我有什么样的方案,喊个口号就可以了,但是对于实际的操作来说并不是这样,我们需要真正的实际的一点点把具体的整体的架构改造做完,首先我们要快速的对整体的架构全面的了解,对所有的功能点都了解以后我们才能把功能点相对打散,站在更高的层面做抽象,然后经过打散和抽象以后我们可以很快的找到切入点,这些一定是目前来说系统中最大的痛点,而且能够给我们现阶段带来收益的最大化,我们整体的架构的改造不可能说让业务完全停滞下来等我们,我们一定是在改造的同时能够为我们的业务带来最大的收益,而且我认为所有的改造不可能是一蹴而就的,一定是一个不断完善的过程,所以我也不追求一步到位,经常有个比较形象的形容就是在高速上边开车边换备胎。

我的目标是寻找一些切入点以后逐渐的借用这些切入点的影响力和切入点明确能够带来的对业务的帮助和效益,逐渐慢慢的把我们的整体架构向正确的方向和更合理的方向不断的完善,不断的演进,在充分了解我们整个百度外卖营销系统以后发现有几个很好的切入点可以快速的帮我们把整体的架构能够梳理的清晰化,这几个切入点对整体的业务也是收益非常大,所以非常适合在现阶段引入,他们分别是第一个消息中心,目前我们整体的用户触达手段有很多,比如说短信,push,小红点,弹窗,UI,这里面我解释一下小红点,就是我们APP里面,想引起你重视的消息可能是会在右上脚有一个小红点,UI的部分主要是抽象我们前面的展示层,比如说对于外卖的营销而言,就是各个商户的名称还有基本信息的下面会显示的商户的营销信息,这个是我们重要的UI的触达手段,在之前的各个系统中其实会有很多系统都会需要用到,刚才所说的各种触达手段,但是每个系统都用到这些触达手段的时候,都需要重新编写,成本是很高的,随着我们用户触达手段的扩充,每扩充一个所有需要用到的系统都需要进行一定的开发,于是我们就抽象出一个消息中心,把所有的用户触达手段统一的封装到消息中心里面去,由营销的逻辑层调用我们的消息中心,进行所有的用户触达手段的调用和使用。

同时我们还发现我们有非常多的营销的资源,比如说代金券,余额,权益,包括第三方的合作的营销资源,比如百度系的爱奇艺的会员兑换码的资源等等,这些资源种类非常的多,这些资源所有的营销工具都是有可能进行发放的,我刚才讲的那个例子中我们有两个功能,比如说下单返券和下单反余额,他们最主要的区别就是下单完成后返的营销资源不同,具体的东西是什么也是可以抽象的,我们也抽象一个发放中心,这个主要也是为了能够封装我们所有的可发放的营销资源,同时我们会发现整个的营销逻辑整体上也是有很多的共通性可以抽象的,我们把整个的营销的逻辑进行大的抽象,主要抽象两个小的模块,一个是优惠计算的逻辑,一个是发放的逻辑,我们看来所有的营销业务无外乎是两个,第一个是对用户进行一些支付金额上的减免,第二个是给用户发放一些发放中心的抽象好的营销资源,当然这些资源在具体使用时还是对金额的减免逻辑,大家可能会发现抽象的概念跟大家平常在写代码中抽象一个接口是一样的,面向对象的高级语言,大家所有人应该都经常会使用抽象的概念把共同的东西统一分装成一个接口,下面有不同的实例的实现。

用来达到一定的解耦,对于抽象这块大家有什么问题都可以具体问,稍候会给大家做一些解答,下面就是要介绍另外一个问题点,隔离,我们也有一个小的概念带引号的危险服务,我的理解危险服务是瞬时压力非常高,存在很大的不确定性的系统,比如我举几个例子,比如说秒杀,抢券的活动,大规模的短信、push,超高的引流落地页,我相信秒杀不用我们太多介绍,会引来,我们业务上刻意会让用户大量的请求瞬时到达我们的服务器端,抢券也是比较常见,这两个不多说了,大规模的短信和触达的手段为什么也是危险的服务,随着现在互联网的应用越来越大,整个互联网的应用的用户基数也是越来越大,所有的几乎的互联网的应用整体的用户的群体和整个的用户的存量基本上都达到一定的级别,做一次全量的短信或者其他触达手段对于所有的系统都是压力非常大的挑战。解释完了危险服务我们再看,刚才这个标题中用了一个名词是共用,是指资源的共用,我的危险的服务和核心的服务共用了基础的资源的时候,由于危险服务的特性,瞬时压力高,不确定性较高,有时候很难完全评估和掌握到我们整个的危险的服务的具体的,比如说他的一个峰值的情况。

当我们的危险服务和我们的核心服务共用一些基础资源,比如说数据库,redisnmq,这里解释一下NMQ,就是百度内部开发使用的一款分布式消息队列,由于危险服务存在瞬时压力很高,不确定性高,我们很难预估准这些危险服务的峰值具体是多少,一旦把我们的危险服务和核心服务有一些共用的话可能是会造成瞬时压力打过来的时候超出我们的预期,比如说我们的一些数据库的资源,我们的数据库连接打满,会造成我们的核心服务和危险服务一起存在异常,

来给大家介绍一些我之前工作经历中遇到的悲惨的经历,比如说第一个是接入层的共用,在那个公司的时候所有的服务的隔离做的非常好了,基本上是我们后面的一些危险的服务都是被完全隔离在一个独立的环境中的,但是只有一个点是没有的,就是我们的接入层,当时觉得接入层的整体的性能是非常高,完全是远超于我们业务所需的努力,所以当时完全没有考虑到接入层这一点可能会出现问题,造成了我们一次巨大的意外,我们做了一次非常大规模,非常大额度的发券活动,引来了大量的用户,远超我们的预期,导致我们接入层被打满,接入层打满以后,包括我们的核心服务的请求也出现大量异常,在这里给大家简单介绍一下接入层是什么。就是我们常见的在我们的基础的服务之前把所有的请求做一个统一的处理,也就是说所有的请求都要先打到接入层再分发给后端的各自的服务。

还比如消息队列应用,所有的用户的短信,还有PUSH做了全量的发放,这个队列 消费完成整整一个小时,虽然在逻辑上是进行MQ的通道的分割,但是他的物理通道只有四个,造成核心服务和所有的危险服务共用这四个物理通道,由于整体一个小时之后才被完成,一个小时之内物理通道上是产生了非常严重的delay,造成核心的消息队列功能,也全部遭到了非常严重的延迟,还有常见的是服务器的共用,在非常高的引流的落地页上严重的超出我们的QPS,这些所有的QPS的预估都是基于我们历史的经验,我们之前给的预估,活动上的东西的预估,尽管我们会去打一些数据量,但是有时候这种不确定性是远远超出我们的预期,可能是会产生非常极高的QPS,这个瞬间会让我们的服务器到零,这台服务器上的很多的应用,所有的服务器上的服务都会异常,对于这种危险的服务我们应该怎么做,整体的架构上有非常通用的方法就是隔离,就是把我们的危险服务隔离在我的核心服务之外,确保即使危险服务出现一些异常或者是问题也不会影响到我们的核心服务。

接下来会给大家讲一些小的例子,这个例子是我们在实验的过程当中针对危险服务做的一个隔离的方式。

大家可以在图上看到这个是有两个机房组成,机房一和机房二,大家可以看到橙色的部分,就是危险的服务,他是简单的活动的落地页,他是活动落地页的服务端和他相应的所使用到的我们的持久化的存储,这块使用的是redis,他是放在了一个独立的机房一中,但是可能大家会想做这个隔离是很难,我们有很多时候我们的被隔离的服务是和我们的核心服务产生一些交互的,这时候我们有两个常见的部门,比如说这里面我给大家举个例子,我们的活动的服务是用不着我们的服务,在这个情况下没有办法能够完全和我们的服务去做一个全隔离的,这样的话我们也就没有办法去做隔离的服务,我们怎么做呢,我们在机房一假设了一个存库,这个是同步我们机房二里面的反作弊的信息,我们只对反作弊的redis从库进行相关的操作,大家主动可以看到我们的活动服务和我们的另外一个核心服务也是会有一些关系,因为你的活动最终会发放一些资源,这里采用的方式是异地化,我们用消息队列做整体的缓冲,确保我们很高的QPS不会直接搭到我们的核心服务,造成产生一些问题,而会采用的是所有的请求最终如果确认需要对用户的代金券进行服务的交互的时候,我们采用异步的方式把消息发给MQ,然后缓存起来,以一个相对匀速的方式,QPS的请求到我们代金券系统进行消费,刚才所讲的是我们常见的针对危险的服务,上面的话是我给大家讲讲对重度 的核心的依赖可能是会造成我们其它的问题。对于相对来说不是特别核心的服务,我们是可以做到一些解耦和降级的。

这里我给大家举个例子,也就是在整个百度外卖比较特殊,他是百度内部自身孵化出来的非常成功的产品,由于他是百度内部孵化出来的,他的整个帐号体系是完全和我们整体的帐号体系是非常紧密的结合在一起的。所有的核心服务几乎都会请求过,去获取到用户的相关的信息,我们就可能是会遇到非常严重的问题是什么,如果服务异常的话,我们所有的核心服务的请求一定都会实现,在所有的业务请求逻辑中这块的信息是非常重要的信息,因为他们要用来明确的标识严用户,所以当这个服务异常的时候会导致整个百度外卖所有的服务全部异常,包括我们最核心的订单服务,其实很可怕的,就是我们用比较直白的语言是怕什么来什么,我们有一次被墨菲定律了,那是大量的passport的服务出现了一些问题,造成我们请求passport服务的时候大规模异常,严重影响了核心服务,风险已经意识到这块耦合度太高,所以我们进行了解耦和降级,对我们的损失非常小,只有在我们自动降级出发的时间之内产生了小的影响。

    大家可以在图中看到我们采用的是缓存的方式来进行的解耦和降级的可能,因为刚才也说了所有的核心服务都会请求passport,获取用户的身份信息,我们选择了一个时间节点,就是在用户提交订单的时候下单完成,从这个取回以后我们让订单系统发送了一条NMQ,用户的身份信息发送给用户缓存下来,这里面使用NMQ也是非常常见的解耦的方式,我们所有的请求都要考虑到大规模和请求量很大的量级的情况下一定会有请求的失败,网络上是有所有的其它的异常的情况,在请求失败的时候,我们的主服务之间,应该是和我们其它的非核心的服务进行严格的解耦,确保我们的主服务不会受到我们其它的服务的拖累,我们会设置一些简单的超时时间,但是你的超时时间的设定也是非常困难的事情,比如说你设置两百毫秒,这二百毫秒也就白白耽误了,与其这样不如能够选择更加解耦的方式做,这里我们就采用NMQ的方式,消息队列,另外系统只是把消息丢给我们的NMQ,而且我们可以接受这种丢失,他是一个用户缓存,即使没有缓存影响也不是很大。

同时大家可以看到另外一个点是一旦当整体的passport服务发生异常的时候我们的降级是所有的业务系统在请求,而是所有的请求全部达到我们的用户缓存上去,通过缓存中起来的用户信息来识别用户。不知道有人知道这样做带来的后果或者是严重的问题是有哪些,首先是给大家解释一下passport的请求是什么,所有的系统做的是类似的,大家基本上都是在用户登陆完成以后会在用户的相关的cookie下种下用户的身份验证票信息,每一次请求到服务端的请求都会带着身份验证信息,和各个系统拿到以后可以去我们的passport的用户的身份的信息中心去领取用户的具体的相关的信息。所以说当我们一旦发生自动降级所有的业务系统不再请求的时候,而是读取我们一个相对不会产生太多变化的用户缓存的时候,我们会带来很多问题,比如说第一个问题,当有一个从来没有在我们这边登陆过的用户在那边登陆来到我们这边的时候,由于用户从来没有在我们系统中登陆,我们是在用户下单的时候把用户的相关信息缓存起来的。

那么我们就没有办法取到用户的缓存信息,就会被一直识别为未登录用户,即使他重新登陆也没有办法在我们这边识别到,另外一个问题对于老用户他如果不是在我们的系统中推出,他是百度系的公用的帐户体系,他在所有的产品包括大家常用的百度的搜索的主页,等等其它的一些业务线,都会采用同样的,他在其它的地方进行了登出,其实是整个完全登出,由于我们做的是用户缓存的,所以我这边也不会得到相关的通知。这些虽然都是问题,但是只要我们了解到问题就可以,鱼和熊掌不可兼得的,我们做的降级一般情况下是有损的,我们确保我们这个可接受的范围内,相对我们降级带来的收益,收益远远大于对用户的损伤是可以接受的。除了刚才讲的小例子以外,所有的降级这块来说常见的都是有两大方案,第一个是减少一些功能,比如说刚才讲的对一些用户造成一些损伤,比如说新用户在我们这边没有办法操作了,这个是减少功能这一块,还有比如说限制流量,耽误系统,系统的承受流量的上线,比如说我给大家介绍前面的内容的时候,讲过严接入层的概念,限制流量经常用在接入层做降级,通过不断的压测和测试,能够知道系统的相对的极限,当知道了我们整个系统可能承受的极限以后我们就会采用限制流量的方式,比如说在我们的接入层限制QPS的峰值,当超过一定的水平线以后我们要对进行抛弃,确保至少可能是会要没有被抛弃的用户可以正常的请求响应,保证不至于整体被打垮。

 

对这块如果有兴趣的同学可以关注我刚才一开始说的公众号,公众号中有一篇非常好的文章是我们这边的OPD开发的nginx+lua的,用来做流量的统计和流量的限流的操作,关键的是他把整个的代码放在里面了,非常的好,希望大家可以拿来主义,可以直接用,你需要简单的调试,操作配置等。工作年限比较多,对于整体的技术做的行业也比较多,各行各业,ERP,一些财务等等都有一些技术去做过,对于我而言,有一些对于技术架构的一些自己的想法和一些感受,第一个是我觉得我们的技术一定是服务于业务的,这个是最优秀的,而且是对于所有的技术人员,至少我们的情况是说我要对业务有一个非常深入的理解以后相对来说更贴和业务的方案,可能有更好的解决方案的实现,有很多问题并不一定是要单靠技术解决的,比如说刚才我们在第二部分讲隔离的时候大家可能是发现有一个点,我们没有具体的说,不知道大家有没有意识到,我们在所有的活动发放代金券的时候我们是走了消息队列,一旦我们的活动异常的火暴,可能是发放代金券的量非常大的时候,非常容易造成我们NMQ的堆积,可能是让用户活动提示他已经获得了一个营销资源的时候,用户是需要自己相关的地方并不一定能够马上看到自己,这时候的问题技术上并不能完全解决,而且技术解决的成本和代价也会很高,这个是有很多问题并不一定单靠技术实现或者是解决。

首先技术一定是无止境的,而且是说没有技术解决不了的问题,只是成本不一定是我们能够接受的,对于这个问题来说我们并不希望投入太大的成本我这块的优化,我们可以很简单的通过业务的方式解决,比如说我们的活动页面简单的提示用户您的代金券 的到帐时间是在几十秒以后或者是一分钟以后或者是说我们在所有的其它的业务中做小规避的方式,第二个点是我觉得作为架构师,负责很多系统的架构师,我们很多时候的工作是站在更高的层面进行抽象和解耦,但是你为了能够站在更高的层面进行相关你要做的抽象和解耦的话,你一定要对整个的系统做一个全面的了解,这个了解需要你真正的站在地上一点点的把这个了解清楚,摸透。刚才也讲了我们有一次痛苦的经历,我们对这个定律击中,我们在之前就意识到了这块服务的偶合是非常严重的,所以我们做了解耦,服务的全面监控危险隔离和自动降级尽量都是在服务上线前或者是至少是在服务上线的初期更全面更好的做到。

还有一个教训是虽然作为一个,我相信很多的人都一定知道主从延迟和并发控制是在编程的时候考虑到的,我很早以前刚入软件这一行的时候,大部分做的系统都是单机的,服务器只有一个,甚至有做CS的系统,但是我们所有人都知道服务器一定是可以以水平有若干个服务器共同组成的,但是主从的延迟和并发的控制一直以来都是经常会反复的问题。这里所说的主从延迟主要是指我们现在由于大部分的业务是会利用到我们的一些存储,我们的一些持久化的工具,比如说像(英文),他们在目前使用的时候,我们一般的业务场景都会是读的请求量远大于写的请求量,所以常见的使用方案是一主带多从,确保我们可以保证在写稳定的同时可以提供更多的有读的可能性,在这时候也同样引入一个问题,主从的延迟,大部分的情况下都是可以忽略,整体都是比较小,在一些并发量比较高包括网络异常的情况下也是会对系统造成摧毁性的打击,尤其是目前到大家一般的开发环境和上线之前的环境,比如说我们也是会用到一些(英文)的数据库,可能是我们部属的时候也是简单的部属一个,所有的读写都在里面,所以不容易在测试的环节和开发的环节发现你开发和测试中遇到的主从延迟的问题,一般这个问题如果是有的话都是在线上发,所以他是非常严重的,花很多心思去注意,最后一个是更深的一个点,我用百度以后我的导师在第五天就给了我一份模板,就是百度的技术设计的模板,那些模板中让我有非常意外的一点,他在让我写技术方案的时候写技术方案一,方案二,一直到方案N,选择的方案包括折中的原因,整个百度系是所有的技术人员都会有这样的,鱼和熊掌不可兼得,要懂得取舍的,在这个变化的过程当中其实我们在所有的软件的架构,统计设计的时候一定要考虑一些实际的情况,比如说刚才在隔离的环节我讲的例子中,活动的(英文),部属在了整个机房里,是在一个里面,其实我们很多的服务是为了防止某一个机房他的整体的对外网络出现一些异常都会采用在多个机房分开部属,确保我们服务的稳定性,但是由于我们做一些隔离需要多做一些服务间的隔离,同时可能是想去在多个机房之间部属,产生了一些矛盾,我们并没有足够的机房可控选择,可能就会做一些折中的考虑。在这里我所说的折中是我放弃了多机房的部属,我没有画出来,我在另外一个机房也就是说我们的核心机房里面留了一个简单的降级页,这个页面非常简单,直接就会返回,而且所有的我们的一个接入层也是独立部属的,确保万一我们的主的活动的服务相关的挂掉了或者是那个主活动的机房,刚才的机房一出现了一些异常的时候,所有的用户还是可以正常倾斜掉我们相关合理的降级页,确保我们整体的服务会产生过大的异常的波动,最后是向大家在说一遍我们百度外卖的技术团队的公众号,我刚才在讲解中说的那样,比如说大家对接入层有兴趣,怎么做限流,包括是在目前很流行的这些技术里面,我们就有文章非常详细的写了相关的技术,也有直接的代码在里面,大家有兴趣的可以去看~




0
相关文章