dubbo接口测试转换成http便捷请求

背景

大部分测试dubbo接口,都是使用Jmeter工具进行测试,需要把jar包下载下来,再根据dubbo接口的传值方法进行测试,对于这一过程及其不方便,有时候会因为dubbo版本与Jmeter插件版本不兼容,导致测试无法进行下去,踩坑较多测试效率较低;为此,本文分享一个dubbo接口测试小工具,大大提高效率!

实现方案

Python + fastapi web框架 + Telnet库

方案原理

dubbo提供了telent命令查看服务,脚本模拟Telnet命令进行dubbo接口测试,通过web框架对Telnet命令进行包装,发送Telnet命令之后,解析数据,通过http接口返回

源码分析

  1. Telnet分解(总结了dubbo接口大致传参类型,如遇特殊的,可联系我)
    def invoke(self, service_name, method_name, arg):
        #自定义对象传参
        if isinstance(arg, dict) and arg:
            command_str = "invoke {0}.{1}({2})".format(
                service_name, method_name, json.dumps(arg))
        #集合对象传参
        elif isinstance(arg, list) and arg:
            command_str = "invoke {0}.{1}({2})".format(
                service_name, method_name, json.dumps(arg))
        #无需对象传值
        elif isinstance(arg, dict) and not arg:
            command_str = "invoke {0}.{1}()".format(
                service_name, method_name)
        #枚举值类型传参
        else:
            command_str = "invoke {0}.{1}({2})".format(
                service_name, method_name, arg)
        data = self.command(command_str)
        try:
            # 字节数据解码 utf8
            data = data.decode("utf-8").split('\n')[0].strip()
        except BaseException:
            # 字节数据解码 gbk
            data = data.decode("gbk").split('\n')[0].strip()
        return data
  1. 模拟command控制台提交
    def command(self, str_=""):
        # 模拟cmd控制台 dubbo>invoke ...
        if self.conn :
            self.conn.write(str_.encode() + b'\n')
            data = self.conn.read_until(self.prompt.encode())
            return data
        else:
            return False

3.支持dubbo接口查询(根据IP地址、端口、服务名查询到对应方法名下的传参类型)

    def ls_invoke(self, service_name):
        command_str = "ls -l {0}".format(service_name)
        data = self.command(command_str)
        if "No such service" in data.decode("utf-8"):
            return False
        else:
            data = data.decode("utf-8").split('\n')
            key = ['methodName', 'paramType','type']
            dubbo_list = []
            for i in range(0, len(data) - 1):
                value = []
                dubbo_name = data[i].strip().split(' ')[1]
                method_name = re.findall(r"(.*?)[(]", dubbo_name)[0]
                value.append(method_name)
                paramType = re.findall(r"[(](.*?)[)]", dubbo_name)[0]
                paramTypeList = paramType.split(',')
                if len(paramTypeList) ==1:
                    paramTypeList = paramTypeList[0]
                value.append(paramTypeList)
                if 'java.lang' in paramType or 'java.math' in paramType:
                    value.append(0)
                elif not paramType:
                    value.append(1)
                elif 'List' in paramType:
                    value.append(2)
                else:
                    value.append(3)
                dubbo_list.append(dict(zip(key, value)))
            return dubbo_list

4.view源码--dubboList

@router.post('/dubboList', name='dubbo列表接口')
async def dubboList(data: DubboListBody):
    host,port = data.url.split(":")
    service_name = data.serviceName
    method_name = data.methodName
    conn = BmDubbo(host, port)
    status = conn.command("")
    #判断是否连接成功
    if status:
        #传入方法名,查询对应方法名的传值类型
        if method_name:
            param_data = conn.param_data(service_name, method_name)
            #判断方法是否存在
            if param_data:
                res_data = {'responseCode': 200, 'responseMsg': "请求成功"}
                dubbo_list = {'responseData': param_data}
                res_data.update(dubbo_list)
                return res_data
            #不存在返回报错
            else:
                return {'responseCode': 301, 'responseMsg': "找不到对应的serviceName"}
        #不传,直接返回服务下所有的数据
        else:
            response_data = conn.ls_invoke(service_name)
            if response_data:
                res_data = {'responseCode': 200, 'responseMsg': "请求成功"}
                dubbo_list = {'responseData':response_data}
                res_data.update(dubbo_list)
                return res_data
            else:
                return {'responseCode': 301, 'responseMsg': "找不到对应的serviceName"}

    #连接不成功返回报错
    else:
        return {'responseCode': 302, 'responseMsg': "dubbo服务连接出错"}

5.view源码--dubboInvoke

@router.post('/dubbo', name='dubbo业务请求接口')
async def dubboInvoke(data: DubboInvokeBody):
    host,port = data.url.split(":")
    service_name = data.serviceName
    method_name = data.methodName
    boby = data.data
    conn = BmDubbo(host, port)
    status = conn.command("")
    if status:
        # 根据服务名和方法名,返回param方法名和类型
        param_data = conn.param_data(service_name, method_name)
        if param_data:
            type = param_data['type']
            param = param_data['paramType']
            # 传参类型为枚举值方法
            if type == 0:
                l_data = [v for v in boby.values()]
                l_data = str(l_data)
                boby = l_data[1:-1]
            # 无需传参
            elif type == 1:
                boby = boby
            # 传参类型为集合对象
            elif type == 2:
                for k, v in boby.items():
                    if isinstance(v, list):
                        boby = v
                        break
            # 传参类型为自定义对象
            else:
                boby.update({"class": param})
            response_data = conn.invoke(service_name, method_name, boby)
            try:
                response_data = json.loads(response_data)
            except Exception as e:
                res_data = {'responseCode': 207, 'responseMsg': "dubbo接口请求异常"}
                res_data.update({'responseData':response_data})
                return res_data
            return response_data
        else:
            return {'responseCode': 301, 'responseMsg': "找不到对应的serviceName"}

    else:
        return {'responseCode': 302, 'responseMsg': "dubbo服务连接出错"}

使用教程

  • pip install requirements.txt
  • 右键运行main文件
  • 访问http://127.0.0.1:5000/api/xxx 即可开始测试dubbo接口
  • 具体传参可看dubbo接口文档
  • 基于fastapi,将dubbo接口转换成便捷的http接口测试
  • 优点:等你来发掘
  • 缺点/bug:等你来发掘

dubbo接口文档

1.查询服务名下的所有方法

接口地址

  • 说明:根据dubbo接口地址和dubbo接口服务名,查询服务名下的所有方法
  • 地址:/api/dubboList
  • 方法:POST

请求头

序号 类型 说明
1 Content-Type application/json JSON 格式

请求体

序号 键值 类型 说明
1 url String dubbo接口地址,IP:端口
2 serviceName String 对应的服务名
3 methodName String 服务名下对应的方法名

请求体示例

{
    "url": "xxx.xxx.xx.xx:20880",
    "serviceName": "cn.com.xxx.mallerp.api.xxxx.xxxx"
}

返回体

序号 键值 类型 说明
1 responseCode Int 返回code
2 responseMsg String 返回信息
3 responseData Array data数组
4 - type int 0-枚举值,1-无需传参
2- 集合对象,3-自定义对象
- paramType string Java传值类型
- methodName string 方法名

返回值示例(成功)

{
    "responseCode": 200,
    "responseMsg": "请求成功",
    "responseData": [
        {
            "methodName": "xxxxxx",
            "paramType": "java.util.HashMap",
            "type": 3
        },
        {
            "methodName": "xxxxxx",
            "paramType": [
                "java.lang.String",
                "java.lang.String",
                "java.lang.String",
                "java.lang.Integer",
                "java.lang.Integer"
            ],
            "type": 0
        },
        {
            "methodName": "xxxxxx",
            "paramType": "",
            "type": 1
        },
        {
            "methodName": "xxxxxx",
            "paramType": "java.util.List",
            "type": 2
        }
    ]
}

返回值示例(失败)

{
    "responseCode": 500,
    "responseMsg": "相应的报错信息"
}

2.dubbo接口-业务接口

接口地址

  • 说明:根据dubbo接口地址和dubbo接口服务名,方法名,参数值实现dubbo接口逻辑
  • 地址:/api/dubbo
  • 方法:POST

请求头

序号 类型 说明
1 Content-Type application/json JSON 格式

请求体

序号 键值 类型 说明
1 url string dubbo接口地址,IP:端口
2 serviceName string 服务名
3 methodName string 方法名
4 data object 传值4种情况,具体看示例

请求体示例 -- 原生对象或者自定义对象传参

{
    "url": "xxx.xxx.xx.xx:20880",
    "serviceName": "cn.com.xxx.mallerp.api.xxxx.xxxx",
    "methodName": "xxxxxx",
    "data": {        //data传入对应的业务json数据
        "productStoreQueryDTOS": [
            {
                "productNoNumDTOList": [
                    {
                        "num": 13,
                        "productNo": "10000620"
                    },
                    {
                        "num": 13,
                        "productNo": "10000014"
                    }
                ],
                "storeCode": "4401S1389"
            }
        ]
    }
}

请求体示例 -- 枚举值类型传参

{
    "url": "xxx.xxx.xx.xx:20880",
    "serviceName": "cn.com.xxx.mallerp.api.xxxx.xxxx",
    "methodName": "login",
    "data": {         //格式为json,顺序必须按照dubbo接口枚举值传参顺序,注意是否为int还是string
        "account":"80563855",
        "password":"3fd6ebe43dab8b6ce6d033a5da6e6ac5"
    }
}

请求体示例 -- 方法名无需传参

{
    "url": "xxx.xxx.xx.xx:20880",
    "serviceName": "cn.com.xxx.mallerp.api.xxxx.xxxx",
    "methodName": "xxxxxx",
    "data":{}      //传入空对象
}

请求体示例 --集合对象传参

{
    "url": "xxx.xxx.xx.xx:20880",
    "serviceName": "cn.com.xxx.mallerp.api.xxxx.xxxx",
    "methodName": "xxxxxx",
    "data":{
        "empList": [
            "30000445",
            "30000444"
        ]
    } //传入对象,里面嵌套数组
}

返回值示例(成功)只展示其中一种

{
    "responseData": "dubbo接口返回什么,就返回什么"
}

返回值示例(失败)

{
    "responseCode": 500,
    "responseMsg": "相应的报错信息"
}

项目地址:dubbo_fastapi

你可能感兴趣的:(dubbo接口测试转换成http便捷请求)