1.      背景介绍

         策略开发人员在完成策略之后,在全流量上线之前要评估新的策略的优劣,常用的评估方法是A-B测试,做法是在全流量中抽样出两份小流量,分别走新策略分支和旧策略分支,通过对比这两份流量下的各指标的差异,我们可以评估出新策略的优劣,进而决定新策略是否全流量。我们把抽样的过程分为流量切分和流量筛选两个步骤。流量切分的逻辑较为复杂,包含多种切分类型和多层嵌套,另外为了保证灵活性,层的组合应该是任意的。

         为了便于流量管理,我们需要将这个过程平台化,这样就需要找到一种形象化的表现方式,将多层流量切分这个过程表现出来,显然这种需求下,简单的输入框、下拉框等控件是不能满足需求的,这就需要构建出可视化的多层流量切分功能,使流量切分这个复杂的动作变得低学习成本、便于理解。

 

图1.1 可视化流量切分示意图

         上图是多层流量切分的一个效果假象图,为了便于理解和管理,我们将多层流量切分过程中使用的到的元素划分为三种:水平层、垂直层、抽样节点这三个元素,为了方便使用,我们会将其封装为基本的控件,供用户直接拖拽使用,下面我们介绍一下各个元素的含义,以方便下文的直接使用。

  •  水平层:水平层是指不进行任何流量的切分的层,它是抽样节点以及子层的承载者,如上面的2、3、4号层。在其内部可以建立子水平层、子垂直层和抽样节点,但是水平层中不能即含有子层又含有抽样节点,不能既含有水平层又含有垂直层。
  •   垂直层:垂直层是用来切分流量的,它将父层的流量切分,变成多个子流量层,这些被切分出来的流量层内部可以采用不同的流量切分类型。垂直层中不能直接包含抽样节点,必须在其内部建立水平层,才能建立抽样节点,在其内部不能直接包含子垂直层,必须建立水平层,例如,图1.1中的5、6、9、10、11号层。
  •  抽样节点:抽样节点是流量切分的流量块单位,每个抽样节点对应着一个流量区间和一个抽样id,抽样id是自增的,且不能修改,同时抽样节点上面可以配置condition,condition的作用就是我们上文提到的流量筛选功能。

2.      架构设计

可视化多层流量切分采用的Flex技术实现,Flex 是一个高效、免费的开源框架,可用于构建具有表现力的 Web 应用程序,这些应用程序利用 Adobe Flash PlayerAdobe AIR, 运行时跨浏览器、桌面和操作系统实现一致的部署。可以通过智能编码、交互式遍历调试以及可视设计用户界面布局等功能加快开发。

可视化多层流量切分功能采用MVC模式实现,其模块结构如下图所示,数据管理中心模块的功能包括数据模型的构建、通过HTTP交互模块与服务端交互数据、算法的封装;逻辑控制模块是数据管理中心与展现模块之间沟通的桥梁,它可以分派用户的请求并选择恰当的展现方式以用于显示,同时它也可以解释用户的操作并将它们映射到数据管理中心模块,数据管理中心模块予以计算。

图1.2 可视化流量切分架构设计

3.      关键模块实现介绍

3.1.  数据管理中心模块设计

上文介绍的多层流量切分功能中用到的基本元素——水平层、垂直层、抽样节点在数据管理中心模块中都建立了相应的数据模型,当初始化程序的时候,数据管理中心模块通过HTTP交互模块与服务端交互获取初始化数据,并将数据填充至基本的数据模型。

那么这些基本元素的数据模型如何实现呢?我们可以从这些元素的特性着手分析,如图1.1所示,整个可视化界面中,将层分为垂直层和水平层,垂直层是如5、6号可以将完整层分割开的层,水平层是如2、3、4号占满父层的层,可以抽象层和数据节点为树结构,可以看作是一种不完全的红黑树,注意这里的红黑树是一种不完全符合规范的红黑树,我们可以将水平层看作为黑色节点,垂直层看作为红色节点,数据节点看作为叶子节点,创建树的时候,我们自动将不包含的抽样节点的空层去掉,那么从图1.2结构就可以转化为图1.3的结构。

图1.3 三种元素在数据管理中心的数据结构

         有了上面的数据结构模型,我们就可以在此基础上封装一些常用的功能函数了,我们下面列举几个封装的计算的例子:

  1. 计算两个抽样节点是否会复用流量:

         两个抽样节点复用流量是指一次请求同时命中了这两个抽样节点,从图1.4上面来看,如果两个抽样节点复用流量,我们形象的表示为,从垂直方向上划一条线,我们约定每一个子层中我们只能够经过一个抽样节点,在这种情况下,每条线可以经过多个抽样节点,也可以不经过任何抽样节点,如果两个抽样节点同时被穿中,就表示这两个抽样节点可能会复用流量。

图1.3 流量复用示意图

 

         判断两个抽样节点是否可能会同时命中比较麻烦,我们可以换一下思维,为什么会有两个节点不能被同时命中,比较容易想到的就是他们位于同一个父层中,这种情况下,两个抽样节点是不可能同时命中的了,还有一种情况就是他们位于不同的父层中也有可能不能同时被命中,究其原因主要是因为他们被垂直层隔开,例如,5、6两个层中的数据节点是不可能同时划中的。按照上面这个思路,我们可以编程序实现判断过程,逻辑如下:

                         i.              判断这两个节点是否位于同一个父层节点中

                       ii.              追溯叶子节点的父节点,一直追溯到根节点,获取到其中红色节点,判断红色节点中是否有兄弟节点。比如,D1->7->5->2->1 红色节点是5, D3->8->6->2->1,红色节点是6,判断红色节点是否是兄弟节点,判断方法为是否父节点相同。

  1. 计算某层中是否可以添加其他元素

         有了上面的数据结构,判断某层中是否可以添加其他元素的实现思路比较简单,按照我们之前的约定,三种元素在某一个父层中是不能共存的,例如,不能在同一个层中即包含水平层又包含垂直层,同时也不能在一个父层中即包含层又包含抽样节点,因此,这里我们的算法实现逻辑就是,获取到该父层中的所有的元素,判读其类别是否唯一即可。

  1. 计算任意两个抽样节点的相互影响概率

         在多层流量切分的架构中,有机会流量复用的抽样节点都是有一定的相互影响的概率,为了方便用户抉择流量分层以及抽样节点的排放位置,我们需要计算出各个抽样节点之间的相互影响概率,计算的数据结果也是基于上面介绍的结构,算法步骤如下:

                         i.              追溯这两个抽样节点的父层路径,一直追溯到共同的父层为止,例如D1可以追溯为D1->7->5->2->1,D8可以追溯为D8->12->9->4->1,这样我们就追溯到了他们的共同的父层1,这里我们首先复用一下上面计算两个抽样节点是否会复用流量的算法,计算一下这两个抽样节点是否可能复用流量。如果没有机会复用流量,则直接返回0%。

                       ii.              如果这两个抽样节点有机会复用流量,按照追溯的父层路径计算相互影响概率,如上面的例子,D1和D8的相互影响概率为:

(D1的流量百分比*7号层的百分比*5号层的百分比*2号层的百分比)*(D8的流量百分比*12号层的百分比*9号层的百分比*4号层的百分比)

  1. 计算层或者抽样节点的插入点

         插入点判断也是基于上面这个数据结构算法封装的算法,它的实现思想是遍历需要插入的父层中的所有的子元素,依据上面的约定,同一父层下的元素类型是唯一的,因此,我们只需要遍历一下各元素之间缝隙,如果可以空间大小可以插入某一元素,则我们可以将该元素插入到指定的位置。

         上面我们举例列举了4种数据管理中心模块中封装的算法,数据管理中心中,根据上面的数据结构,我们还可以封装出其他的数据基础算法,供逻辑控制模块使用。

3.2.  展现模块的设计

         展现模块封装了展现绘图的基本方法,实现的思想是从小粒度到整体的渲染过程,首先,我们实现了三种基本元素的展现样式,例如,垂直层和水平层是统一的展现样式,它的基本绘图单元是矩形,垂直层和水平层为了做区分,它们的填充色是不一样的,抽样节点的基本绘图单元是图片,抽样节点再每种不同的状态下,展现的图片是不一样的。

         展现模块接收的绘图数据格式是上面介绍的树状结构,因此,展现界面渲染其实就是一个树的遍历的过程,我们采用的遍历方式是前序遍历,例如,如果传如的数据格式为图1.2的形式,那么前序遍历的结果就是:

1->2->5->7->D1->D2->6->8->D3->D4->3->D5->D6->D7->4->9->12->D8->D9->15->D15->10->13->D10->D11->16->D16->11->14->D12->D13->D14

1.动态渲染过程中的元素大小自动调节问题

         渲染过程会有一个问题,这是从外向内的一种渲染方式,我们必然会先固定一个最外层的高度和宽度,那么这样就会导致初始设定的高度或宽度过小,放不下内部的各个元素,针对这个问题,首先为了处理简单,我们可以将每层的高度固定下来,宽度是可变的,基于这种前提,我们处理上面这个问题的方案有二,一种是预先计算好渲染所需要的宽度,另一种是方法是绘图过程中动态的调整最外层的宽度,如果数据量比较大的情况下,选用方案一的效率较高,如果数据量不大,这两种方案的差异是不大的,两种渲染方式的实现逻辑如下图所示。

图1.4 动态渲染过程中的元素大小自动调节方案,预先计算结果(左),渲染过程中自动调节(右)

2.控件拖拽与双击的实现

         控件的拖拽与双击实现比较简单,主要通过事件的方式来实现,预先在绘图面板上面增加了onCanvasMouseDown(鼠标按下),onCanvasMouseMove(鼠标移动),onIconMouseDown(控件鼠标按下),onIconDoubleClick(控件双击)等事件,在不同的情况下展现的样式做出调整即可。

by randy_yao