ABTest产品有哪些变化?不是我们随便“拍脑袋”得到的,而是需要以实际数据为驱动,让用户的反馈指导我们如何更好的改进服务。正如马蜂窝CEO陈刚在接受采访时所说:“有些东西需要Sense,但大多数东西可以用Science来判断。”说到ABTest,相信很多读者都不陌生。简单来说,ABTest就是将用户分成不同的群体,同时在线测试不同版本的产品,通过用户反馈的真实数据,找出哪个版本更好的过程。我们以原始版本作为对照组,本着对每个版本进行尽可能小的流量迭代的原则使用ABTest。指标分析完成后,将全面推出用户反馈数据最好的版本。很多时候,一个按钮、一张图片或者一句文案的调整,都可能带来非常明显的增长。下面是一个ABTest在马蜂窝的应用案例:如图所示,我们的搜索团队和电商团队想优化一个关于“滑雪”的搜索列表。可以看出优化前的页面显示感觉比较单薄。但是,大家不确定更复杂的展示形式会不会让用户觉得不够简洁而反感。因此,我们将改版前后的页面放到线上进行ABTest。最终数据反馈显示,优化后的款式UV提升了15.21%,转化率提升了11.83%。使用ABTest帮助我们降低了迭代的风险。通过这个例子,我们可以更直观的了解ABTest的几个特点:优先级:采用流量切分和小流量测试的方式,先让一些流量小的线上用户来验证我们的想法,然后根据数据反馈给促进全流,减少产品损耗。Parallelism:我们可以同时运行两个或多个版本的实验进行比较,保证每个版本的环境是一致的,这样之前整个季度才可以确定是否发布版本,但是现在可能只需要一周的时间,避免流程复杂、周期长的问题,节省验证时间。科学性:ABTest在统计测试结果时,需要使用统计指标来判断结果是否可行,避免我们依赖经验主义来做决定。为了使我们的验证结论更加准确、合理、高效,我们按照谷歌的惯例实施了一套算法保障机制,严格实现流量的科学分配。基于Openresty的多层分布模型,大部分公司的ABTests提供接口,业务方获取用户数据然后调用接口。这样会使原来的流量翻倍,业务入侵更加明显。支撑场景比较单一,导致需要针对多个业务方的需求开发很多分发系统,难以针对不同场景进行复用。为了解决以上问题,我们的卸载系统基于Openresty实现,卸载信息通过HTTP或GRPC协议传输。这样分发系统工作在业务的上游,由于Openresty的流量分发特性,不会产生二次流量。对于业务端,只需要在不侵入业务的情况下提供差异化??服务即可。选择Openresty做ABTest的主要原因有以下几点:在设计ABTest系统的时候,我们将整体流程拆分为三个要素,第一是确定的终端,包含设备和用户信息;第二个是确定的URI;三是匹配的分配策略,即如何分配流量。首先,设备发起请求,AB网关从请求中提取设备ID、URI等信息。此时终端信息和URI信息已经确定。然后遍历URI信息匹配相应的策略。请求通过分流算法找到当前匹配的AB实验和版本后,AB网关会通过两种方式通知下游。对于运行在物理Web机器上的应用程序,会在header中添加一个名为abtest的key,其中包含最新的AB实验和版本信息。对于微服务应用,注册的微服务信息会被添加到cookie中,由微服务网关进行处理。稳定的分流保障:MurmurHash算法分流算法我们采用MurmurHash算法,参与算法的哈希因子包括设备id、策略id、流量层id。MurmurHash是ABTest在业界常用的一种算法。可以应用到很多开源项目,比如Redis、Memcached、Cassandra、HBase等。MurmurHash有两个明显的特点:速度快,比安全哈希算法快几十倍,而且变化足够激烈。对于类似的字符串,比如“abc”和“abd”,可以将它们均匀分布在哈希环上,主要是为了实现正交和互斥实验的分流下面简单解释一下正交和互斥:互斥。意味着两个实验的流量是独立的,用户只能进入其中一个实验。一般用于同一海量层的实验,如图文混合列表实验、纯图像列表实验。一个用户一次只能看到一个实验,因此它们是互斥的。正交的。正交是指进入所有实验的用户之间没有必然关系。例如,实验一中进入版本a的用户在进行其他实验时是均匀分布的,而不是集中在某个区间内。流层实验将流层实验的哈希因子分为设备id和流层id。当一个请求流过一个流量层时,该层只会激活一个实验,即同一用户的同一个请求在每一层最多只会激活一个实验。首先对哈希因子进行哈希运算,使用murmurhash2算法,可以保证哈希因子变化很小但结果的值变化很大,然后对100的余数加1,最后得到一个1到100之间的值。示意图如下:实验中版本分发实验的hash因子包括deviceid、policyid、trafficlayerid。使用相同的策略进行版本匹配。匹配规则如下:稳定性保证:刚才提到的多级缓存策略,每次请求到来后,系统都会尝试获取匹配的实验策略。实验策略是从后台配置的,我们通过消息队列将配置好的策略同步到我们的策略池中。我们最初的计划是每次请求过来后,从Redis中读取数据。这样的话Redis的稳定性就高了,大量的请求也会对Redis造成比较大的压力。因此,我们引入多级缓存机制,形成策略池。策略池分为三层:第一层lrucache,是一种简单高效的缓存策略。其特点是存在Nginxworker进程的生命周期,专供worker使用,效率很高。由于独占的特点,每个cache都会存在于每个worker进程中,因此会占用较多的内存。第二层lua_shared_dict,顾名思义,这个缓存可以跨worker共享。它的数据不会在Nginx重新加载时丢失,只有在重新启动时才会丢失。但是有一个特点。为了安全读写,实现了读写锁。所以在某些极端情况下可能会出现性能问题。第三层Redis。从整套策略来看,虽然采用了多级缓存,但还是存在一定的风险,即当一级缓存和二级缓存都失效时(比如Nginx重启),Redis可能会面临“裸奔”,因为太多的流量。风险,这里我们使用lua-resty-lock来解决这个问题。当缓存失效时,只有获取到锁的那部分请求可以返回给源头,保证Redis的压力不会那么大。我们统计缓存30s情况下的线上数据,一级缓存命中率在99%以上,二级缓存命中率0.5%,请求回源到Redis只有0.03%.主要特点吞吐量:目前承担全站5%的流量低延迟:平均在线延迟小于2ms全平台:支持App、H5、WxApp、PC、跨语言容灾:自动降级:读取策略时fromredis失败后,ab会自动进入非拆分模式,然后每隔30s(每台机器)尝试读取redis,直到数据读取完毕,避免频繁发送请求和手动降级:当server_event日志过多或者系统负载过高,关闭所有实验或通过后台“一键关闭”关闭AB分流性能。从响应时间来看,除了一开始请求偏差比较大,之后平均在1ms以内。分析之初差距较大的原因是当时多级缓存中没有数据。TPS的压测性能有一些小幅下降,因为毕竟有hash算法,但总体在可以接受的范围内。A/B发布常规的A/B发布主要由API网关完成。当面临的业务需求更复杂时,A/B发布通过与微服务交互,发布更复杂维度的A/B发布能力。小结需要注意的是,ABTest并不完全适用于所有产品,因为ABTest的结果需要大量的数据支持,日流量越大的网站结果越准确。一般来说,我们建议在进行A/B测试时,每个版本的日流量应该在1000UV以上,否则测试周期会很长,或者很难得到准确(结果收敛)的数据和结果推论。要设计一个完整的ABTest平台需要做大量细致的工作。限于篇幅,本文只关注分布算法。综上所述,马蜂窝的ABTest分发系统在以下几个方面取得了一些成果:采用流量拦截分发的方式,摒弃了原有接口形式,不侵入业务代码,对性能无明显影响,无二次二次交通。通过采用流量分层和绑定实验的策略,可以更精细、更直观地定义导流实验。通过与客户端上报已安装实验版本的机制,减少业务数据的存储,实现串口实验分流的功能。在数据传输方面,通过在HTTP头中加入导流信息,业务方无需关心具体的实现语言。最近的规划改进:监控系统。用户画像等AB的精细化定制。统计功效支持置信区间和特征值等乘积函数。通过AARRR模型评估实验对北极星指标的影响。未来该系统还有很多需要改进的地方,我们将继续探索,期待与您的交流。本文作者:李培,马蜂窝基础平台信息技术研发专家;张立虎,马蜂窝酒店研发静态数据团队工程师。【本文为专栏作者马蜂窝科技原创文章,作者微信公众号马蜂窝科技(ID:mfwtech)】点此查看作者更多好文
