Jmock实践

HamletKent 发布于2月前

背景

随着业务的发展,技术架构也在不断的演变升级,为了实现快速、稳定的响应上层业务的需求,各个业务点也在不停的组件化,京东零售的整体技术架构纵向使用分层解藕,横向进行多业务拆分,这样极大的调动整体技术架构的灵活性,也能加快业务的迭代。就拿京东商城来说,它的需要支持的端就包含PC、M站、App、微信手Q等业务线,每条业务线下面需要融合网关系统、搜索系统、支付系统、商品系统、广告系统、促销系统、订单系统、商家系统、供应链系统、物流系统等。大体架构我们可以用一张图来说明一下。

研发辅助神器——Jmock实践

随着系统架构的不断升级增加、组织架构的调整人员替换交接,也出现了一些问题:

1) 中台很多系统都没有测试环境,导致很多团队负责的应用无法在本地(开发人员机器)运行,开发的功能无法及时得到测试,极大的影响了需求开发的效率;

2) 由于依赖系统不存在测试环境,测试团队在接到测试需求时候,无法在测试环境对测试需求进行测试,极大的影响测试人员的工作效率;因为依赖系统没有对应的测试环境,导致目前的开发、测试模式均是将代码部署至预发环境,功能变更到部署至预发耗时较 长(公司没有成熟的CI/CD流程),导致整个需求交付周期变长。同时,研发、测试共用一套预发环境,也导致多个需求并行的的时候存在冲突,进一步降低需求交付效率。

3) 需求的排期需要牵涉更多的系统,单个需求原来需要一个团队开发完成即可上线,现在需要多个多个团队各自开发,不同团队开发的系统之间相互依赖,开发效率各不相同,很多时候,我们都在等待依赖系统提供环境然后验证自己的系统功能是否正确,整个需求交付效率就更加低效。

综合目前面临的问题,我们需要找到一款产品能解决以下几个痛点:服务依赖缺失、应用无法启动、mock接入流程复杂、手动mock的操作繁琐。Jmock平台的产生由此而来,我们可以从下图中了解Jmock平台在架构的位置:

研发辅助神器——Jmock实践

Jmock是如何解决以上痛点我们将在下面的章节中给大家详细讲解。

 

一步接入

01

client的SDK接入

为了实现应用接入流程复杂,JMock的接入仅仅需要引入Maven依赖即可,完全不需要任何其他的客户端配置。

 

<!-- JMock refer: http://test.jmock.jd.com -->
     <dependency>
         <groupId>com.jd.m.mocker</groupId>
         <artifactId>mocker-client</artifactId>
         <version>x.x.x-beta.x</version>
     </dependency>

对于需要mock的本地方法只需在方法上添加@JMock注解即可。

 

@JMock
     public String foo(String param) {
         return param;
     }

另外,SDK会自动校验应用环境是否符合mock条件,如果是生产环境机器,SDK会主动关闭开关,不会产生任何不必要的性能损耗及风险。

0 2

零配置

JMock遵循零配置原则,做到尽量减少研发的使用、学习成本,JMock的零配置主要体现在3个方面:

1) 基础数据零配置:Mock所用到的接入方应用中包括使用者、应用名称等基础数据会由SDK自动获取;

2) JSF调用零配置:JMock采用JSF自身提供的扩展点支持来实现JSF调用过程的零配置,mock过程在JSF的filter层完成;

3) 应用健康监控零配置:应用的启动过程的健康情况会随Spring的生命周期由SDK上报,并自动注册第一次接入的新应用

智能mock

01

网络隔离

为了方便大家快速使用,减少用户手动mock的繁琐操作,内置了智能化的mock开关逻辑。mock-client发现当前网络不属于开发环境、测试环境的时候,会自动关闭mock。此时对应用毫无影响。

0 2

mock-client维度的mock开关

SDK维度的mock开关用来控制全局,默认开启mock,不需要配置。当用户现式的将其关闭之后,应用的mock动作直接停止,配置方法为在应用的classpath目录下新建文件mocker.properties,并写入以下配置。

<span> <span># 关闭JMock</span></span>

<span> mocker.<span>on</span>=<span>false</span></span>

03

mock-server维度的开关

在mock-server中区分应用级别的mock开关和方法级别的开关。应用级别的mock开关一旦关闭,此应用下所有的方法均不再进程mock操作;方法维度的mock开关,顾名思义就是控制当个方法是否进行mock操作,一旦关闭,只会影响这个方法是否进行mock。如果要实现智能mock,就需要将应用mock开关和方法mock开发都是开启状态,默认开启状态,再配合mock数据源控制,这个在下一节中会说到。交互关系我们通过下面一张图来进行说明:

研发辅助神器——Jmock实践

04

数据源智能控制

mock数据来源用来控制方法返回值生成策略,也是智能mock实现的关键之一。我先说明一下几个数据源:

1) 历史数据:也称为缓存数据,也就是当项目启动的时候,上一次拉取的线上数据,保存到缓存数据表中;

2) 自动生成:强制mock操作,通过返回值模版来生成mock数据,在应用级别和方法级别都有这个数据源的控制;

3) 智能选择:智能选择是在服务优先请求服务端接口,拉取数据,如果接口不存活,就会到缓存数据表中拉取数据,如果缓存数据表也不存在,那就直接使用mock生成返回值数据;

4) 后端依赖:强制请求服务端接口拉取返回值数据;

5) 同应用来源:这个是方法数据来源中的控制中出现,如果使用这个来控制数据来源的化,那就会同步到应用级别的数据来源控制;

总体而言,私有模版的数据源控制优先级最高,接着就是应用模板数据源控制,之后是公有模板数据源的控制,最后是应用级别的数据源控制,我们可以通过以下这张图来说明它们直接的逻辑。

研发辅助神器——Jmock实践

05

mock开关和mock数据源的关系

Mock开关控制应用、方法是否进行Mock,一旦关闭,则执行方法原有逻辑。Mock开关开启的时候,才会按照Mock数据来源的规则进行处理。通过两者灵活设置来实现mock自动化生成,如下图展示:

研发辅助神器——Jmock实践

数据自动生成

数据的自动生成不仅减少用户需要手动配置的流程,还可以为用户自动解析并mock出日益复杂的项目依赖的服务接口,实现对接口的自动生成需要解决两方面的问题,第一是对类的结构解析,第一个就是选择一个通用的占位符规范,生成标准的json模板,再解析json模板生成客户端需要的json字符串。如下下图所示,具体的实现方式我们将在下面的内容中介绍。

研发辅助神器——Jmock实践

01

类结构解析

研发辅助神器——Jmock实践

1)通过JavaTypeStructParser.parse来解析类的结构和每个字段的类型

解析结果示例如下:

{
       "fields": [
         {
           "name": "id",
           "struct": {
             "range": {
               "max": 9223372036854775807,
               "min": -9223372036854775808,
               "type": "RANGE"
             },
             "structType": "NOR",
             "targetClassName": "java.lang.Long",
             "valueType": "NUM"
           },
           "structType": "FIELD"
         },
         {
           "name": "appName",
           "struct": {
             "structType": "NOR",
             "targetClassName": "java.lang.String",
             "valueType": "STR"
           },
           "structType": "FIELD"
         }
       ],
       "structType": "OBJ",
       "targetClassName": "com.jd.jone.api.v2.beans.sysapp.AppWithSysInfo"
     }

2)再通过JtspTpl.parse(struct)根据每个字段类型,为字段生成对应的占位符

解析结果示例:

{
       "id": "@natural",
       "appName": "@last",
       "@type": "com.jd.jone.api.v2.beans.sysapp.AppWithSysInfo"
     }

3)通过Mockingly.mock,生成mock数据替换占位符,生成JSON格式的返回值返回给SDK。返回值示例:

{
       "id": 36322613,
       "appName": "White",
       "@type": "com.jd.jone.api.v2.beans.sysapp.AppWithSysInfo"
     }

4)SDK拿到JSON格式返回值反序列化生成Mock结果对象

02

占位符规范

占位符用于自动生成mock参数,目前有数值型、字符型、布尔型、日期型。占位符是借鉴MockJS部分占位符,不是所有的MockJS占位符都支持,占位符语法参考MockJS数据占位符定义规范。占位符 只是在属性值字符串中占个位置,并不出现在最终的属性值中。占位符 的格式为:

 

<span><span>@占位符</span></span>

<span><span>@占位符(参数[,参数])</span></span>

1)使用说明:

①用 @ 来标识其后的字符串是 占位符。比如:识别@first占位符,如果填成@firsttest,将识别为firsttest,需要使用@first()test才能识别。

②占位符不区分大小写。

③占位符 目前无法引用 数据模板 中的属性。

④占位符目前无法支持 相对路径 和 绝对路径。

⑤你可以同时使用多个占位符,比如例子中的‘name.full'属性值。

{
         name: {
             first: '@FIRST',
             middle: '@FIRST',
             last: '@LAST',
             full: '@first @middle @last'
         }
     }// 填充后 =>
     {
         "name": {
             "first": "Charles",
             "middle": "Brenda",
             "last": "Lopez",
             "full": "Charles Brenda Lopez"
         }
     }

2)部分占位符展示:

研发辅助神器——Jmock实践

3) 占位符扩展

我们在现在已经支持的占位符基础上,可以进行增删改查,定期的通过接口使用的场景,会自动将一些占位符添加到到占位符库中。

服务依赖分析

服务依赖分析能协助研发清楚知道哪些接口服务是通过mock自动生成,哪些接口是存活拉取的线上数据。应用启动之后,从client端请求到mock-server端,传递参数和方法声明,在mock-server端我们会根据传递的数据调用jsf的服务。判断接口是否存活分为两部分,首先会根据方法声明在jsf注册中心验证是否注册,如果未注册,表明该方法声明不存活,如果已经注册,就会根据该jsf接口能否在1000ms内返回方法调用预期的数据即为可用,否则也会判定为不存活,并将接口声明的存活状态反馈给研发,协助研发快速定位依赖接口的问题。操作流程如下图:

研发辅助神器——Jmock实践

未来展望

Jmock能帮助开发和测试快速启动本地应用,实现研发人员的降本提效,同时也提供了应用依赖分析,协助开发能快速的找到接口依赖的问题。目前Jmock平台已经完成了应用mock的数据智能选择、返回值模板的管理、应用依赖分析等,后续迭代我们将重点实现mock的参数化,全链路以及与测试用例的对接。

咨询邮箱: holdsupport@jd.com

查看原文: 研发辅助神器——Jmock实践

  • organicduck
  • beautifulostrich
  • TommyFrederic
  • GroteDaisy
  • ToynbeeDonald
  • RobTheodore