1. A/B 测试服务端分流 API 接入

实现分流 API 接入主要包含两步工作:

  1. 服务端调用试验的分流 API 接口,并根据 API 返回结果进行自定义业务逻辑。
  2. 服务端使用神策埋点采集 SDK 进行 $ABTestTrigger 事件的上报。

整体流程示意如下图所示:

  1. 客户端正常发起对服务端的 API 业务请求。
  2. 服务端拿到客户端的用户 ID 等相关信息去调用神策分流 API 接口。
  3. 服务度正常获取到神策分流 API 返回的相应结果。
  4. 根据分流 API 返回的结果,决定执行不同的试验策略,并将试验的策略结果返回给客户端。
  5. 服务端上报 $ABTestTrigger 事件到神策分析系统。
  6. 客户端的神策埋点 SDK 正常上报其它已埋点的事件到神策分析系统当中。

1.1. API 请求说明

1.1.1. API 请求地址

请求的 URL 地址和 project-key 参数可以从试验创建完成后生成的 SDK 示例代码中找到,如下图所示:

获取到的 URL 地址和 project-key 为

POST http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E
CODE

1.1.2. 请求参数

必须为 application/json 格式,没有标注必填的均为可选项。

{
    "anonymous_id":"6YF7YAZCw8qRQ4eL",  // 匿名 ID(必填)
    "login_id":"10012",                 // 登录 ID
    "platform":"API",                   // 请求平台,固定值(必填)
    "properties": {
        "$country":"中国",               // 国家,默认从 IP 地址解析
        "$province":"湖北",              // 省份,默认从 IP 地址解析
        "$city":"武汉",                  // 城市,默认从 IP 地址解析
        "$carrier":"中国联通",            // 运营商,默认从 IP 地址解析
        "$ip":"10.10.22.33"             // 客户端 IP 地址
        "$is_first_day": false          // 是否为首日访问
    }
}
JS

注意事项:

  • 如果没有填写国家、省份、城市、运营商等信息,且分流试验中需要用到相关信息,则分流服务会默认会从客户端 $ip 中解析,注意这里传递的 $ip 为客户端的 ip 地址
  • 这里没有列出所有的 properties 参数,如需要传递更多的客户端信息,请联系神策技术支持。

1.1.3. 返回结构

{
    "status": "SUCCESS",                        // 查询结果标志(SUCCESS:进行试验;FAILED:不进入试验)
    "error_type": "XXX_ERROR",                  // 错误类型,请求结果为 FAILED 时返回
    "error":"xxxxxx",                           // 错误描述信息
    "results":
     [
         {
            "abtest_experiment_id":"111"        // 试验 ID
            "abtest_experiment_group_id":"123", // 试验分组 ID
            "is_control_group":false,           // 是否是对照组
            "is_white_list": true,              // 是否白名单用户,白名单用户不进行试验事件的上报
            "variables": [
                {
                    "name": "color",            // 变量名称
                    "value": "1",               // 变量值
                    "type": "INTEGER"           // 变量类型 INTEGER/STRING/BOOLEAN/JSON
                }
            ]
        }
    ]
}
JS

1.2. 请求示例

1.2.1. curl 请求示例

curl 'http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E' \
    --header 'Content-Type: application/json' \
    --data-binary '{
      "anonymous_id": "6YF7YAZCw8qRQ4eL",
      "login_id": "10012",
      "platform": "API",
      "properties": {
        "$ip":"10.10.22.33",
        "$is_first_day": false
      }
    }'
CODE

1.2.2. Java 代码示例

ObjectMapper objectMapper = new ObjectMapper();
// 使用 ConcurrentLoggingConsumer 初始化 SensorsAnalytics
final SensorsAnalytics sa = new SensorsAnalytics(new SensorsAnalytics.ConcurrentLoggingConsumer("您的日志文件路径"));
 
// 准备调用数据
Map<String, Object> data = new HashMap<>();
data.put("anonymous_id", "6YF7YAZCw8qRQ4eL");
data.put("login_id", "10012");
data.put("platform", "API");
Map<String, Object> properties = new HashMap<>();
properties.put("$ip", "10.10.22.33");
properties.put("$is_first_day", true);
data.put("properties", properties);
String jsonData = objectMapper.writeValueAsString(data);
 
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
 
URI uri = URI.create("http://abtesting.saas.debugbox.sensorsdata.cn/api/v2/abtest/online/results?project-key=438B9364C98D54371751BA82F6484A1A03A5155E");
 
HttpPost httpPost = new HttpPost();
httpPost.setURI(uri);
httpPost.setEntity(new StringEntity(jsonData, ContentType.APPLICATION_JSON));
 
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
 
Map<String, Object> response = objectMapper.readValue(httpResponse.getEntity().getContent(), new TypeReference<Map<String, Object>>() {});
 
if (httpResponse.getStatusLine().getStatusCode() == 200) {
  if ("SUCCESS".equals(response.get("status"))) {
    List<Map<String, Object>> results = (List<Map<String, Object>>) response.get("results"); // 试验命中结果
    for (Map<String, Object> result : results) {
      String abtestExperimentId = (String) result.get("abtest_experiment_id"); // 命中的试验id
      String abtestExperimentGroupId = (String) result.get("abtest_experiment_group_id"); // 命中的试验分组id
      Boolean isControlGroup = (Boolean) result.get("is_control_group"); // 是否是对照组
      Boolean isWhiteList = (Boolean) result.get("is_white_list"); // 是否是白名单用户,白名单用户不需要进行事件的上报
 
      List<Map<String, Object>> variables = (List<Map<String, Object>>) result.get("variables"); // 试验变量对象
      for (Map<String, Object> variable : variables) {
        String name = (String) variable.get("name"); // 变量名称
        String value = (String) variable.get("value"); // 变量值
        String type = (String) variable.get("type"); // 变量类型,INTEGER/STRING/BOOLEAN/JSON
      }
      // 根据试验变量, 进行相关具体业务

      // 使用 SDK 上报 $ABTestTrigger 事件,完整服务端 SDK 使用文档:https://manual.sensorsdata.cn/sa/latest/tech_sdk_server-1573789.html
      Map<String, Object> properties = new HashMap<String, Object>();
      properties.put("$abtest_experiment_id", abtestExperimentId); // 试验 ID,必传属性
      properties.put("$abtest_experiment_group_id", abtestExperimentGroupId); // 试验分组 ID,必须属性
      sa.track(distinctId, true, "$ABTestTrigger", properties);
    }
  } else {
    // 进入试验失败,获取错误信息,建议执行对照组策略
    String errorType = (String) response.get("error_type"); // 错误类型
    String error = (String) response.get("error"); // 错误描述信息
  }
} else {
  // 状态码不为 200,则系统异常,建议执行对照组策略,有需要请联系神策技术支持
  String errorType = (String) response.get("error_type"); // 错误类型
  String error = (String) response.get("error"); // 错误描述信息
  String requestId = (String) response.get("request_id"); // 请求 id
}
JAVA