# 系统架构

  • 整体架构图

  • 服务编排架构图

# 服务清单

模块 工程名 端口
网关节点 fizz-gateway-node 8600
管理后台 fizz-manager-professtional 8000

# 中间件清单

资源类型 数据库
Redis 10
MySQL fizz-manager

# 数据库设计

# Mysql

# E-R图

  • 服务编排

FizzGate管理后台-aggregate[服务编排]

  • 路由

FizzGate管理后台-api[路由]

  • 流控

FizzGate管理后台-flow_control[流控]

  • 开放文档

FizzGate管理后台-open_doc[开放文档]

# 中间件

# Redis

key key中文名 值类型 描述
fizz_api_config_route api配置 hash api配置存放Hash的Key
fizz_api_config_app_set_size api配置app集合个数 hash api配置app集合个数存放Hash的Key
fizz_aggregate_config 聚合配置 hash 聚合配置存放Hash的Key
fizz_rpc_service RPC服务信息 hash RPC服务信息存放Hash的Key
fizz_app 应用权限 hash RPC服务信息存放Hash的Key
fizz_gateway_group 网关分组 hash 网关分组存放Hash的Key
fizz_plugin_config 插件 hash 插件存放Hash的Key
fizz_api_pairing_doc 文档集配对配置 hash 文档集配对配置存放Hash的Key
fizz_rate_limit 流控配置 hash 流控配置存放Hash的Key
fizz_api_config_app:%s_%s api配置app set api配置app存放Set的Key模式{fizz_api_config_app:apiId_index}
fizz_manager_url 管理后台地址 string 管理后台地址
fizz_api_pairing_info 配对信息 hash 配对信息存放Hash的Key
fizz_global_resource 公共参数 hash 公共参数存放Hash的Key

# MQ设计

消息类型 TAG GID BODY字段描述 说明
Redis fizz_api_config_channel_route api配置Channel
Redis fizz_api_config_app_channel api配置app关联Channel
Redis fizz_plugin_config_channel 插件固定配置Channel
Redis fizz_aggregate_channel 聚合配置Channel
Redis fizz_rpc_service_channel RPC服务信息Channel
Redis fizz_app_channel 应用权限Channel
Redis fizz_gateway_group_channel 网关分组Channel
Redis fizz_api_pairing_doc_channel 文档集配对配置Channel
Redis fizz_rate_limit_channel 流控配置Channel
Redis fizz_global_resource_channel 公共参数Channel
Redis fizz_api_pairing_info_channel 配对信息Channel

【说明】

  • 消息类型:kafka、rocketMq、activeMq、Redis
  • TAG*:指的是Topic*
  • GID*:指的是消费组*
  • BODY*:消息具体的格式,为便于扩展,通常为JSON格式。消费者需要考虑兼容JSON消息体可能扩展的情况*
  • 说明:描述消息

# 服务编排设计

# 配置文件设计

// 接口配置
export const aggrAPIConfig = {
  name: "input name", // 自定义的聚合接口名
  debug: false, // 是否为调试模式,默认false(v1.1.4后已废弃使用,于列表统一设置debugMode)
  type: "REQUEST", // 类型,REQUEST/MYSQL
  method: "GET", // HTTP方法,GET/POST等
  path: "/aggr/aggr-hotel/hotel/rates", // 格式:/aggr/+分组名+路径, 分组名以aggr-开头,表示聚合接口
  headersDef: { // 可选,定义聚合接口header部分参数,使用JSON Schema规范(详见:http://json-schema.org/specification.html),用于参数验证,接口文档生成
    "type": [
      "object",
      "null"
    ],
    "properties": {
      appId: {
        type: "string",
        title: "应用ID",
        description: "描述"
      }
    },
    "required": ["appId"]
  },
  paramsDef: { // 可选,定义聚合接口parameter部分参数,使用JSON Schema规范(详见:http://json-schema.org/specification.html),用于参数验证,接口文档生成
    "type": [
      "object",
      "null"
    ],
    "properties": {
      lang: {
        type: "string",
        title: "语言",
        description: "描述"
      }
    },
    "required": []
  },
  bodyDef: { // 可选,定义聚合接口body部分参数,使用JSON Schema规范(详见:http://json-schema.org/specification.html),用于参数验证,接口文档生成
    "type": [
      "object",
      "null"
    ],
    "properties": {
      userId: {
        type: "string",
        title: "用户名",
        description: "描述"
      }
    },
    "required": ["userId"]
  },
  // TODO (所有脚本相关 都是同一处理) 脚本内容另外存储 传参用脚本id替换
  scriptValidate: { // 可选,用于headersDef、paramsDef、bodyDef无法覆盖的入参验证场景
    "aggregate_config_script_id": 12
    // 以下是脚本具体内容
    // type: "", // groovy
    // source: "", // 脚本返回List<String>对象,null:验证通过,List:错误信息列表
    // consts: { // 常量
    // },
    // variables: { // 环境变量
    // }
  },
  validateResponse: {// 入参验证失败响应,处理方式同dataMapping.response
    fixedBody: { // 固定的body
      "code": -411
    },
    fixedHeaders: {// 固定header
      "a": "b"
    },
    headers: { // 引用的header
    },
    body: { // 引用的header
      "errorMsg": "string validateMsg", // 默认值
    },
    script: {
      "aggregate_config_script_id": 2
    }
  },
  langDef: {
    "langParam": "input.request.body.languageCode",
    "langMapping": {
      "zh": "0",
      "en": "1"
    }
  },
  dataMapping: {// 聚合接口数据转换规则
    response: {
      fixedBody: { // 固定的body
        "code": "b"
      },
      fixedHeaders: {// 固定header
        "a": "b"
      },
      headers: { // 引用的header,默认为源数据类型,如果要转换类型则以目标类型+空格开头,如:"int "
        "abc": "int step1.requests.request1.headers.xyz"
      },
      body: { // 引用的header,默认为源数据类型,如果要转换类型则以目标类型+空格开头,如:"int "
        "abc": "int step1.requests.request1.response.id",
        "inn.innName": "step1.requests.request2.response.hotelName",
      },
      script: { // 脚本计算body的值
        "aggregate_config_script_id": 1
        // 脚本内容另外存储 用脚本id替换
        // type: "", // groovy
        // source: ""
      }
    }
  },
  "edges": [ // 连接列表
    {
        "properties": {}, // 连线属性
        "sourceNodeName": "START_100", // 连接开始节点
        "targetNodeName": "REQUEST_1" // 连线结束节点
    }
    // ...
  ],
  "nodes": [ // 节点列表
    {
        "properties": {}, // 节点自定义属性
        "type": "START", // 节点类型
        "name": "START_100", // 节点标识ID
        "text": {
            "value": "开始" // 节点名称
        }
    },
    {
      "properties": { // 节点自定义属性
        "dataMapping": { // 数据转换规则
          "request": {
              "contentType": "application/json",
              "fixedHeaders": [],
              "headers": [],
              "fixedParams": [],
              "params": [
                  {
                      "key": "mobile",
                      "value": "input.request.params.mobile"
                  }
              ],
              "script": {}
          },
          "response": {
              "contentType": "auto",
              "fixedHeaders": {},
              "headers": {},
              "fixedBody": {},
              "body": {},
              "script": {}
          }
        },
        "name": "REQUEST_1", // 节点标识ID
        "condition": {}, 
        "components": [], // 组件配置
        "fallback": {},
        "type": "REQUEST", // 节点类型
        "method": "GET", // 请求方法
        "protocol": "http", // 协议
        "serviceType": 2, // 服务类型
        "serviceName": "api-example", // 服务名
        "path": "/proxy/api-example/getMobileInfo", // API路径
        "nodeSize": {
            "width": 220,
            "height": 100
        }
      },
      "type": "REQUEST",
      "name": "REQUEST_1",
      "x": 400,
      "y": 190
    },
    
    {
      "properties": {},
      "name": "END_101",
      "type": "END",
      "x": 930,
      "y": 190,
      "text": {
          "value": "结束"
      }
    }
  ],
  stepConfigs: [{ // step的配置
    name: "step1", // 步骤名称
    stop: false, // 是否在执行完当前step就返回
    dataMapping: {// step response数据转换规则 配置结果
      response: {
        fixedBody: { // 固定的body
          "a": "b"
        },
        body: { // step result
          "abc": "step1.requests.request1.response.id",
          "inn.innName": "step1.requests.request2.response.hotelName"
        },
        script: {// 脚本计算body的值
          "aggregate_config_script_id": 1
          // 脚本内容另外存储 用脚本id替换
          // type: "", // groovy
          // source: ""
        }
      }
    },
 
    // 添加组件
    components: [
      {
        "type": "condition",
        "desc": "如:判断是否会员",
        "value1": {
          "type": "fixed/ref",
          "fixedDataType": "",  // 固定值的数据类型: number/string/boolean
          "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
          "value": null
        },
        // 比较符: eq gt ge lt le ne contains notContain containsAny isNull isNotNull isBlank isNotBlank isEmpty isNotEmpty
        "operator": "",
        "value2": {
          "type": "", // 固定值:fixed   引用值: ref
          "fixedDataType": "",  // 固定值的数据类型: number/string/boolean
          "refDataType": "",  // 引用值的数据类型:int/long/float/double/string/boolean/array
          "value": null
        }
      },
      {
        "type": "circle",
        "desc": "", // 描述 如:循环调用10次
        "dataSourceType": "", // 固定值:fixed   引用值: ref
        "dataSource": "",  // int or string
        // 循环条件
        "execConditions": [
          {
            "type": "condition",
            "desc": "",  // 描述: 如:判断是否会员
            "value1": {
              "type": "fixed/ref",
              "fixedDataType": "", // 固定值的数据类型: number/string/boolean
              "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
              "value": null
            },
            // 比较符: eq gt ge lt le ne contains notContain containsAny isNull isNotNull isBlank isNotBlank isEmpty isNotEmpty
            "operator": "",
            "value2": {
              "type": "fixed/ref",
              "fixedDataType": "", // 固定值的数据类型: number/string/boolean
              "refDataType": "",  // 引用值的数据类型:int/long/float/double/string/boolean/array
              "value": null
            }
          }
        ],
        // 退出条件
        "breakConditions": [
          {
            "type": "condition",
            "desc": "如:判断是否会员",
            "value1": {
              "type": "fixed/ref",
              "fixedDataType": "", // 固定值的数据类型: number/string/boolean
              "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
              "value": null
            },
            //比较符: eq gt ge lt le ne contains notContain containsAny isNull isNotNull isBlank isNotBlank isEmpty isNotEmpty
            "operator": "",
            "value2": {
              "type": "fixed/ref",
              "fixedDataType": "",  // 固定值的数据类型: number/string/boolean
              "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
              "value": null
            }
          }
        ]
      }
    ],
 
    requests: [ //每个step可以调用多个接口
      {
        name: "request1", // 接口名,格式request+N
        type: "REQUEST", // 类型,RESTful接口-REQUEST | Dubbo接口-DUBBO | gRPC接口-GRPC
        serviceName: 'com.abc.UserService', // [Dobbo/gRPC接口]
        version: '1.2', // 仅Dubbo
        group: '', // 仅Dubbo
        paramTypes: 'int,java.lang.String,com.abc.entity.Depart',// 仅Dubbo
        method: "GET", // [RESTful接口] HTTP方法,GET/POST等
        // 'method': 'getUserInfo', // [Dobbo/gRPC接口] 自定义方法:如'getUserInfo' 已有复用字段
        url: "", // [仅RESTful接口] 默认url,当环境url为null时使用
        devUrl: "http://baidu.com", // 仅RESTful接口
        testUrl: "http://baidu.com", // 仅RESTful接口
        preUrl: "http://baidu.com", // 仅RESTful接口
        prodUrl: "http://baidu.com", // 仅RESTful接口
        timeout: 3000, // 超时时间 单位毫秒,允许1-10000秒之间的值,不填或小于1毫秒取默认值3秒,大于10秒取10秒
        condition: { // 预处理 脚本配置
          "aggregate_config_script_id": 1
        },
        fallback: {
          mode: "stop", // stop|continue当请求失败时是否继续执行
          defaultResult: "" // 当mode=continue时,可设置默认的响应报文(json string)
        },
        dataMapping: { // 数据转换规则
          request: {
            fixedBody: {},
            fixedHeaders: {},
            fixedParams: {},
            headers: {//默认为源数据类型,如果要转换类型则以目标类型+空格开头,如:"int "
              "abc": "step1.requests.request1.headers.xyz"
            },
            body: {
              "*": "input.request.body", // * 用于透传一个json对象
              "inn.innId": "step1.requests.request1.response.id" // 默认为源数据类型,如果要转换类型则以目标类型+空格开头,如:"int "
            },
            params: {//默认为源数据类型,如果要转换类型则以目标类型+空格开头,如:"int "
              "userId": "input.requestBody.userId"
            },
            script: {// 脚本计算body的值
              "aggregate_config_script_id": 1
              // 脚本内容另外存储 用脚本id替换
              // type: "", // groovy
              // source: ""、
            }
          },
          response: {
            fixedBody: {},
            fixedHeaders: {},
            headers: {
              "abc": "step1.requests.request1.headers.xyz"
            },
            body: {
              "inn.innId": "step1.requests.request1.response.id"
            },
            script: {// 脚本计算body的值
              "aggregate_config_script_id": 1
              // 脚本内容另外存储 用脚本id替换
              // type: "", // groovy
              // source: ""
            }
          },
          // 添加组件 (请求内)
          components: [
            {
              "type": "condition",
              "desc": "如:判断是否会员",
              "value1": {
                "type": "fixed/ref",
                "fixedDataType": "",  // 固定值的数据类型: number/string/boolean
                "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
                "value": null
              },
              // 比较符: eq gt ge lt le ne contains notContain containsAny isNull isNotNull isBlank isNotBlank isEmpty isNotEmpty
              "operator": "",
              "value2": {
                "type": "", // 固定值:fixed   引用值: ref
                "fixedDataType": "",  // 固定值的数据类型: number/string/boolean
                "refDataType": "",  // 引用值的数据类型:int/long/float/double/string/boolean/array
                "value": null
              }
            },
            {
              "type": "circle",
              "desc": "", // 描述 如:循环调用10次
              "dataSourceType": "", // 固定值:fixed   引用值: ref
              "dataSource": "",  // int or string
              // 循环条件
              "execConditions": [
                {
                  "type": "condition",
                  "desc": "",  // 描述: 如:判断是否会员
                  "value1": {
                    "type": "fixed/ref",
                    "fixedDataType": "", // 固定值的数据类型: number/string/boolean
                    "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
                    "value": null
                  },
                  // 比较符: eq gt ge lt le ne contains notContain containsAny isNull isNotNull isBlank isNotBlank isEmpty isNotEmpty
                  "operator": "",
                  "value2": {
                    "type": "fixed/ref",
                    "fixedDataType": "", // 固定值的数据类型: number/string/boolean
                    "refDataType": "",  // 引用值的数据类型:int/long/float/double/string/boolean/array
                    "value": null
                  }
                }
              ],
              // 退出条件
              "breakConditions": [
                {
                  "type": "condition",
                  "desc": "如:判断是否会员",
                  "value1": {
                    "type": "fixed/ref",
                    "fixedDataType": "", // 固定值的数据类型: number/string/boolean
                    "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
                    "value": null
                  },
                  //比较符: eq gt ge lt le ne contains notContain containsAny isNull isNotNull isBlank isNotBlank isEmpty isNotEmpty
                  "operator": "",
                  "value2": {
                    "type": "fixed/ref",
                    "fixedDataType": "",  // 固定值的数据类型: number/string/boolean
                    "refDataType": "", // 引用值的数据类型:int/long/float/double/string/boolean/array
                    "value": null
                  }
                }
              ]
            }
          ]
        }
      }
    ]
  }]
};

# 上下文设计

// 上下文数据结构设计
// 上下文,用于保存客户输入输出和每个步骤的输入与输出结果
var context = {
    // 是否DEBUG模式
    debug: false,
    // 各个操作的耗时
    elapsedTimes: [{
        [actionName]: 123, // 操作名称:耗时
    }],
 
    // exception info
    exceptionMessage: "",
    exceptionStacks: "",
    exceptionData: "", 
 
    // 客户输入和接口的返回结果
    input: {
        request: {
            path: "",
            method: "GET/POST",
            headers: {},
            body: {},
            params: {}
        },
        response: { // 聚合接口的响应
            headers: {},
            body: {}
        }
    },
    node: {
      [node id]: {
        request: {
          url: "",
          method: "GET/POST",
          headers: {},
          body: {}
        },
        // 根据转换规则转换后的接口响应
        response: {
          headers: {},
          body: {}
        },
        // 请求循环组件当前循环对象
        item: null,
        // 请求循环组件当前循环对象的下标
        index: null,
        // 请求循环的结果
        circle: [
          {
            // 循环对象
            item: null,
            // 循环对象的下标
            index: null,
            // 请求相关参数
            request: {
                url: "",
                method: "GET/POST",
                headers: {},
                body: {}
            },
            // 根据转换规则转换后的接口响应
            response: {
                headers: {},
                body: {}
            }
          }
        ],
        // 条件组件的执行结果
        conditionResults: [
          {
              '我的条件1': true
          }
        ]
      }
    },
    // 步骤
    step1: {
        requests: { // 接口1
            request1: { // 请求相关参数
                request: {
                    url: "",
                    method: "GET/POST",
                    headers: {},
                    body: {}
                },
                // 根据转换规则转换后的接口响应
                response: {
                    headers: {},
                    body: {}
                },
                // 请求循环组件当前循环对象
                item: null,
                // 请求循环组件当前循环对象的下标
                index: null,
                // 请求循环的结果
                circle: [{
                    // 循环对象
                    item: null,
                    // 循环对象的下标
                    index: null,
                    // 请求相关参数
                    request: {
                        url: "",
                        method: "GET/POST",
                        headers: {},
                        body: {}
                    },
                    // 根据转换规则转换后的接口响应
                    response: {
                        headers: {},
                        body: {}
                    }
                }],
                // 条件组件的执行结果
                conditionResults: [
                    {
                        '我的条件1': true
                    }
                ]
            },
            // 接口2
            request2: {
                // ... 字段网关接口1
            }
            //... 其它接口,字段同上
        },
        // 步骤结果
        result: {},
        // 步骤循环组件当前循环对象
        item: null,
        // 步骤循环组件当前循环对象的下标
        index: null,
        // 步骤循环的结果
        circle: [{
            // 循环对象
            item: null,
            // 循环对象的下标
            index: null,
            // 步骤结果
            result: {}
        }]
    }
}