柯南平台开源版本,为用户提供流量回放全流程解决方案

Related tags

Miscellaneous conan
Overview

柯南流量回放平台

MIT License Vue2.0 Vue2.0 Author Live Demo

经过在线教育业务中的持续打磨与迭代,柯南平台终于开源,旨在为行业内更多的的质效保障团队提供更专业更稳定的质效保障方案。随着业务与技术架构的不断变化,服务端的质量保障工作变得越来越复杂。近几年流量回放的方案在行业内落地生根,但大部分以工具为主并且使用成本与二次开发生成本较高,柯南平台应运而生。

目标

基于线上真实用户流量的录制回放能力与结果校验能力,为冒烟测试,集成回归测试,线上验证与线上巡检提供解决方案。

核心功能

流量采集

基于ES日志源的流量录制采集,平台化配置接入,降低使用成本,并且提供详细的流量采集数据。

流量回放

分布式的后端架构,为流量回放提升执行效率,支持服务鉴权配置,基于http协议的回放符合真实业务场景。

结果校验

流量回放的常规校验方式基本上是以流量结果的DIFF为主,但大量的流量噪声(时间戳,自增数据...)一直影响结果的准确性,柯南平台在回放中基于配置的jsonSchema做第一层校验,再结合自研的降噪比对服务进行流量DIFF的第二层校验,从而保障了结果校验的准确性,大大提升了流量回放结果的可信度。

平台优势与应用场景

优势

  • 解决传统自动化覆盖率低,维护成本高的问题
  • 多规则的流量结果断言校验
  • 多规则的流量结果比对支持
  • 流量数据可用于自动化测试与性能测试
  • 交互简单,配置化接入
  • 开源共建,持续优化

应用场景

  • 提测质量卡点
  • CI/CD流水线质量卡点
  • 服务线上监控巡检

平台业务架构
后端业务架构.png

业务架构

平台技术架构
后端技术架构.png

服务端架构

平台能力及功能 柯南能力图.png

使用须知

  • 流量采集: ES日志;
  • 回放协议: http协议;
  • 具体环境可参考开源详细技术文档

写在最后

质效的提升也许不能单单通过一个平台,技术与人的结合才能带来更大的突破。善于利用技术创新才能从容的面对越来越频繁的需求,越来越复杂的业务,柯南平台的技术方案产出于学而思网校的大班业务并且逐步通用化,平台现已开源,希望更多优秀的人或团队参与进来,为质效保障工作提供更多的解决方案。

详细使用文档 https://dengkunnanmayun.gitee.io/conan-docs/#/use/README

更多介绍 https://mp.weixin.qq.com/s/1Cvi5kkqfF9y1rBi97qLwg



项目负责人-李宁



项目成员-刘劲松 胡耀国 邓坤楠 纪莹



柯南官方QQ群


Comments
  • 怎么录制回放带有 queryString 的 get 接口?

    怎么录制回放带有 queryString 的 get 接口?

    在接口管理里配置的接口名称:/api/case/list

    在 域名管理中的 es 配置:

    ES中_source内接口名称对应的原始值:GET /api/case/list?pageSize=10&pageNum=1&productLineId=1&caseType=0&channel=1&bizId=83b0bcf4

    接口正则表达式:/.*(?=\?)

    录制完毕后,回放时,提示如下错误:

    2021-06-22 05:24:39 [INFO]  --开始执行回放, 回放ID:replay_id=12,task_execution_id=32,record_id=32\n2021-06-22 05:24:44 [INFO]   --回放url=http://127.0.0.1:8094/api/case/list,body=,response={code:10400,msg:Required Integer parameter 'channel' is not present,data:null}
    2021-06-22 05:24:44 [INFO]   --回放url=http://127.0.0.1:8094/api/case/list,body=,response={code:10400,msg:Required Integer parameter 'channel' is not present,data:null}
    ...
    

    错误原因为 url 里缺失了 queryString 部分。查看源码发现,回放时接口请求完整url拼接逻辑为:

    com.tal.wangxiao.conan.agent.service.impl.ReplayServiceImpl#doReplay

    ...
    Optional<Api> apiOptional = apiRepository.findById(apiId);
    ...
    String domainName = domainOptional.get().getName();
                String method = EnumUtil.getByField(HttpMethodConstants.class, "getValue", String.valueOf(api.getMethod())).getLabel();
                String urlStr = "http://" + domainName + api.getName();
    ...
    response = replayByHttp(urlStr, method, body, headerMap);
                    log.info("回放url={},method={},body={},response={}", urlStr, method, body, response);
    

    逻辑里面,url 用的是 api.getName() ,即数据库中的接口名称。这个名称是不会带有 queryString 的,所以导致回放时缺失这部分。

    是我使用姿势不对吗?还是目前不支持带有 QueryString 的 Get 接口录制回放?

    opened by chenhengjie123 1
  • ES 查询条件配置的 ”ES中_source内接口名称“ ,进行流量获取时过滤条件要求包含 http method 不大合理

    ES 查询条件配置的 ”ES中_source内接口名称“ ,进行流量获取时过滤条件要求包含 http method 不大合理

    特指此配置项: image

    不合理原因: 在录制时,com.tal.wangxiao.conan.agent.service.impl.RecordServiceImpl#getFlowByEsClientAndSaveFLow 和此参数有关的 过滤条件为 + 空格 +

    BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                    .must(QueryBuilders.wildcardQuery(url + ".keyword", method + " " + requestQuery.getApi() + "*"))
                    .must(shouldQuery);
    

    但同样在录制逻辑中,实际入库完整 request 内容的 com.tal.wangxiao.conan.agent.service.impl.RecordServiceImpl#saveFlowInDb ,查询条件变为了纯 api path ,相关逻辑:

    ...
    String apiKey = esConditionSetting.getApi();
    ...
    String apiName = RegexUtils.getMsgByRegex(esFlowMap.get(apiKey) + "", esConditionSetting.getApiRegex());
    ...
    List<Api> apiList = apiRepository.findByNameAndMethod(apiName, HttpMethodConstants.valueOf(method).getValue());
    

    这样会导致如果没有配置 "接口正则表达式" ,且保证可以过滤掉 http method ,就会导致获取到的 apiName 为类似 GET /api/case/list 的值,导致无法匹配数据库中已定义的接口,进而出现录制是成功的(条数是对的),但实际没有任何数据入库到 bss_record_result 表(因为没有匹配到任何已定义的接口),没法回放

    是否可以调整为,条数的查询条件里,去掉 http method 合并到 url 的做法,而是单独多开一个查询条件,专门查 http method ?本身 es 配置里也有要求配置单独对应 http method 的字段了,应该是可以适用的

    opened by chenhengjie123 1
  • 针对域名的鉴权逻辑支持类型不足

    针对域名的鉴权逻辑支持类型不足

    当前的Web系统的认证类型较多,当前的系统仅仅支持 采用 header里 使用 Cookie的类型的。 比如我的产品使用了AKSK认证。 是在header中增加了Authorization参数。且认证逻辑比较复杂。

    [AKSK认证信息]参考 https://support.huaweicloud.com/devg-apisign/api-sign-provide-aksk.html

    opened by happybeta 1
  • 功能优化及修复

    功能优化及修复

    1. 修复根据ES key迁移DB时,获取不到key抛出 NullPointerException 空指针异常问题
    2. 优化第一次从容器中获取Bean失败报错误日志问题
    3. 修复设置数据源时抛异常问题
    4. 优化admin和agent加载bean主库时只有enaled为true时才加载功能
    5. 修复新增和更新bss_es_condition_setting时es_source_id错误的问题
    6. 修复根据域名导出ES源配置规则时导出excel为空问题
    7. 修复根据域名导出ES源配置规则时导出excel的字段显示错误问题
    8. 优化日志输出路径及日志名称
    9. 前端优化域名管理绑定ES源参数输入框不能输入问题
    10. 优化application配置文件
    opened by xzyxyz 0
  • 录制异常,异常code:Unable to parse response body

    录制异常,异常code:Unable to parse response body

    2022-01-27 10:24:39.615 [流量回放线程-8] INFO  c.t.w.conan.agent.service.impl.RecordServiceImpl - scroll_id=FGluY2x1ZGVfY29udGV4dF91dWlkDnF1ZXJ5VGhlbkZldGNoAA==
    2022-01-27 10:24:39.615 [流量回放线程-8] INFO  c.t.w.conan.agent.service.impl.RecordServiceImpl - 查询到总流量0条
    2022-01-27 10:24:39.615 [流量回放线程-8] INFO  c.t.w.conan.agent.service.impl.RecordServiceImpl - 查询到总流量 length0
    ElasticsearchStatusException[Unable to parse response body]; nested: ResponseException[method [DELETE], host [http://10.33.40.22:9200], URI [/_search/scroll], status line [HTTP/1.1 404 Not Found]
    {"succeeded":true,"num_freed":0}];
            at org.elasticsearch.client.RestHighLevelClient.parseResponseException(RestHighLevelClient.java:1872)
            at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1626)
            at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1583)
            at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1553)
            at org.elasticsearch.client.RestHighLevelClient.clearScroll(RestHighLevelClient.java:1225)
            at com.tal.wangxiao.conan.agent.service.impl.RecordServiceImpl.getFlowByEsClientAndSaveFLow(RecordServiceImpl.java:379)
            at com.tal.wangxiao.conan.agent.service.impl.RecordServiceImpl.record(RecordServiceImpl.java:262)
            at com.tal.wangxiao.conan.agent.service.impl.RecordServiceImpl.startRecord(RecordServiceImpl.java:208)
            at com.tal.wangxiao.conan.agent.service.impl.RecordServiceImpl$$FastClassBySpringCGLIB$$12c31d87.invoke(<generated>)
            at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
            at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:771)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
            at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
            at com.alibaba.druid.support.spring.stat.DruidStatInterceptor.invoke(DruidStatInterceptor.java:73)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
            at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
            at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
            at com.tal.wangxiao.conan.agent.service.impl.RecordServiceImpl$$EnhancerBySpringCGLIB$$c9e46460.startRecord(<generated>)
            at com.tal.wangxiao.conan.agent.config.KafkaConsumerListener.record(KafkaConsumerListener.java:99)
            at sun.reflect.GeneratedMethodAccessor238.invoke(Unknown Source)
            at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
            at java.lang.reflect.Method.invoke(Method.java:498)
            at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198)
            at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:774)
            at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
            at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
            at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
            at java.util.concurrent.FutureTask.run(FutureTask.java:266)
            at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
            at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
            at java.lang.Thread.run(Thread.java:748)
            Suppressed: ParsingException[Failed to parse object: expecting field with name [error] but found [succeeded]]
                    at org.elasticsearch.common.xcontent.XContentParserUtils.ensureFieldName(XContentParserUtils.java:50)
                    at org.elasticsearch.ElasticsearchException.failureFromXContent(ElasticsearchException.java:592)
                    at org.elasticsearch.rest.BytesRestResponse.errorFromXContent(BytesRestResponse.java:179)
                    at org.elasticsearch.client.RestHighLevelClient.parseEntity(RestHighLevelClient.java:1892)
                    at org.elasticsearch.client.RestHighLevelClient.parseResponseException(RestHighLevelClient.java:1869)
                    ... 31 more
    Caused by: org.elasticsearch.client.ResponseException: method [DELETE], host [http://10.33.40.22:9200], URI [/_search/scroll], status line [HTTP/1.1 404 Not Found]
    {"succeeded":true,"num_freed":0}
            at org.elasticsearch.client.RestClient.convertResponse(RestClient.java:302)
            at org.elasticsearch.client.RestClient.performRequest(RestClient.java:272)
            at org.elasticsearch.client.RestClient.performRequest(RestClient.java:246)
            at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1613)
            ... 30 more
    2022-01-27 10:24:39.621 [流量回放线程-8] ERROR c.t.w.conan.agent.config.KafkaConsumerListener - 录制异常,异常code:Unable to parse response body
    
    
    opened by happybeta 0
Owner
好未来技术
好未来开源
好未来技术