架构师效率快的终极原因:Fizz网关之服务编排

2020-10-30 14:16:47 浏览数 (1)

Fizz网关简介

Fizz Gateway 是一个基于 Java开发的微服务网关,能够实现热服务编排、自动授权选择、线上服务脚本编码、在线测试、高性能路由、API审核管理等目的,拥有强大的自定义插件系统可以自行扩展,并且提供友好的图形化配置界面,能够快速帮助企业进行API服务治理、减少中间层胶水代码以及降低编码投入、提高 API 服务的稳定性和安全性。

什么是服务编排

服务编排主要基于现有的业务微服务使用在线配置的方式快速的生成一个聚合接口。

特点: 在线API设计、在线测试、快速开发

##举例说明

订单详情页面需要展示订单信息、商品信息和用户信息。可通过配置的方式生成一个接口先后调用底层微服务的订单详情接口、商品信息接口和用户信息接口,再从这3个接口的返回结果里提取需要的字段返回给前端页面。

服务编排架构

fizz_aggregate.jpgfizz_aggregate.jpg

服务编排是基于配置文件,每个接口对应一个配置文件,配置信息可通过管理后台的可视化界面来生成。服务编排是一个pipeline的架构,基于配置文件动态生成。一个pipeline由输入,输出和中间的步骤组成。一个pipeline可配置多个步骤(step),一个步骤可配置一个或多个请求(request);步骤是串联执行,每个步骤里的请求是并发执行。

功能介绍

在之前的文章里已经通过一个小例子简单了解过服务编排功能了,下面介绍一下服务编排的主要功能特殊。

  • 接口配置化,动态生成
  • 数据转换和字段映射
  • 支持动态脚本

接口配置化

服务编排是通过配置来定义一个新接口,配置采用json格式编写,以下是前面介绍过的小例子的配置:

代码语言:txt复制
{
  "name": "会员资产信息",
  "debug": false,
  "type": "REQUEST",
  "method": "GET",
  "path": "/proxy/member-assets/assets/getAssetsInfo",
  "headersDef": {
    "type": "object",
    "properties": {},
    "required": []
  },
  "paramsDef": {
    "type": "object",
    "properties": {
      "memberId": {
        "type": "integer",
        "title": "会员ID"
      }
    },
    "required": [
      "memberId"
    ]
  },
  "scriptValidate": {},
  "validateResponse": {
    "fixedHeaders": {},
    "headers": {},
    "fixedBody": {
      "code": 400
    },
    "body": {
      "msg": "validateMsg"
    },
    "script": {}
  },
  "stepConfigs": [
    {
      "name": "step1",
      "stop": false,
      "requests": [
        {
          "name": "request1",
          "type": "REQUEST",
          "fallback": {},
          "condition": {},
          "dataMapping": {
            "request": {
              "fixedHeaders": {},
              "headers": {},
              "fixedParams": {},
              "params": {
                "memberId": "input.request.params.memberId"
              },
              "script": {}
            },
            "response": {
              "fixedHeaders": {},
              "headers": {},
              "fixedBody": {},
              "body": {},
              "script": {}
            }
          },
          "conditionFlag": false,
          "method": "GET",
          "url": "http://points-service/points/getMebPoints",
          "timeout": "3000"
        },
        {
          "name": "request2",
          "type": "REQUEST",
          "fallback": {},
          "condition": {},
          "dataMapping": {
            "request": {
              "fixedHeaders": {},
              "headers": {
                "memberId": "input.request.params.memberId"
              },
              "fixedParams": {},
              "params": {},
              "script": {}
            },
            "response": {
              "fixedHeaders": {},
              "headers": {},
              "fixedBody": {},
              "body": {},
              "script": {}
            }
          },
          "conditionFlag": false,
          "method": "GET",
          "url": "http://coupon-service/coupon/getCouponCount",
          "timeout": "3000"
        }
      ],
      "dataMapping": {}
    }
  ],
  "dataMapping": {
    "response": {
      "fixedHeaders": {},
      "headers": {},
      "fixedBody": {
        "code": 0,
        "msg": "ok"
      },
      "body": {
        "data.points": "step1.request1.response.body.data.points",
        "data.coupons": "step1.request2.response.body.data.count"
      },
      "script": {}
    }
  },
  "langDef": {
    "langParam": "",
    "langMapping": {
      "zh": null,
      "en": null
    }
  }
}

要配置的内容比较多,如果要人手编写编写的话又很容易出错,人手编写的前提还得对配置里每个字段都有一定的了解才可以。为了解决这个问题fizz在管理后台提供了一个可视化界面来生成接口的配置文件,只须按表单提示填写相应内容即可。下面我们来了解一下配置界面和配置的对应关系,但不逐个字段介绍配置信息,因为后续主要是通过界面来维护配置信息。

基础信息

config_basic_info.pngconfig_basic_info.png
代码语言:txt复制
{
  "name": "会员资产信息",
  "type": "REQUEST",
  "method": "GET",
  "path": "/proxy/member-assets/assets/getAssetsInfo"
}

配置输入

配置输入是用来定义一个接口的请求头,请求体(仅POST请求需要配置)和URL的queryString参数,以及相关的校验规则。定义和校验遵循JSON Schema规范,详见:

http://json-schema.org/specification.html

http://json-schema.org/understanding-json-schema/

config_input.pngconfig_input.png
代码语言:txt复制
{
  "headersDef": {
    "type": "object",
    "properties": {},
    "required": []
  },
  "paramsDef": {
    "type": "object",
    "properties": {
      "memberId": {
        "type": "integer",
        "title": "会员ID"
      }
    },
    "required": [
      "memberId"
    ]
  },
  "scriptValidate": {},
  "langDef": {
    "langParam": "",
    "langMapping": {
      "zh": null,
      "en": null
    }
  }
}

配置步骤

config_step.pngconfig_step.png
代码语言:txt复制
{
  "stepConfigs": [
    {
      "name": "step1",
      "stop": false,
      "requests": [
        {
          "name": "request1",
          "type": "REQUEST",
          "fallback": {},
          "condition": {},
          "dataMapping": {
            "request": {
              "fixedHeaders": {},
              "headers": {},
              "fixedParams": {},
              "params": {
                "memberId": "input.request.params.memberId"
              },
              "script": {}
            },
            "response": {
              "fixedHeaders": {},
              "headers": {},
              "fixedBody": {},
              "body": {},
              "script": {}
            }
          },
          "conditionFlag": false,
          "method": "GET",
          "url": "http://points-service/points/getMebPoints",
          "timeout": "3000"
        },
        {
          "name": "request2",
          "type": "REQUEST",
          "fallback": {},
          "condition": {},
          "dataMapping": {
            "request": {
              "fixedHeaders": {},
              "headers": {
                "memberId": "input.request.params.memberId"
              },
              "fixedParams": {},
              "params": {},
              "script": {}
            },
            "response": {}
          },
          "conditionFlag": false,
          "method": "GET",
          "url": "http://coupon-service/coupon/getCouponCount",
          "timeout": "3000"
        }
      ],
      "dataMapping": {}
    }
  ]
}

配置输出

config_output.pngconfig_output.png
代码语言:txt复制
{
  "dataMapping": {
    "response": {
      "fixedHeaders": {},
      "headers": {},
      "fixedBody": {
        "code": 0,
        "msg": "ok"
      },
      "body": {
        "data.points": "step1.request1.response.body.data.points",
        "data.coupons": "step1.request2.response.body.data.count"
      },
      "script": {}
    }
  }
}

校验结果

校验结果的配置主要用于在入参数校验不通过时返回给接口调用方的响应报文或响应头:

  • 校验不通过时,Fizz会把校验失败的原因(如:会员ID不能为空)放到上下文的validateMsg字段里
  • 可以自定义返回给调用方的报文格式,如 code, msg
  • 支持自定义响应头
  • 支持自定义脚本处理校验结果
config_validate_resp.pngconfig_validate_resp.png
代码语言:txt复制
{
  "validateResponse": {
    "fixedHeaders": {},
    "headers": {},
    "fixedBody": {
      "code": 400
    },
    "body": {
      "msg": "validateMsg"
    },
    "script": {}
  }
}

数据转换和字段映射

支持配置固定值,引用值和脚本这三种类型的数据映射。通过它们可以完成服务编排接口、request接口入参的拼装和返回结果的加工处理等。

固定值

固定值主要用于需要硬编码的场景,如:下游接口的固定入参

aggr_config_step_mapping_1.pngaggr_config_step_mapping_1.png

引用值

引用值主要用于在各个步骤间或从输入引用数据,通常一个request的入参都会引用到输入的值或上一步骤(step)的结果。通过一个简单的引用路径就可以取到相应的值,同时可以指定数据类型做强制类型转换。

aggr_config_step_mapping_2.pngaggr_config_step_mapping_2.png

脚本

支持给字段配置脚本,字段的返回返回结果作为字段的值

aggr_config_step_mapping_3.pngaggr_config_step_mapping_3.png
aggr_config_step_mapping_4.pngaggr_config_step_mapping_4.png

星号 *

星号通配符可以接收一个返回对象类型的引用值,返回对象里的字段会合并到目标对象里

aggr_config_step_mapping_5.pngaggr_config_step_mapping_5.png

样例:userInfo = {"userName": "Fizz", "userID": 1234}

优先级与覆盖顺序

固定值 < 引用值 < 脚本 < 星号*

当一个字段配置了多种类型的值时按以上顺序覆盖,星号优先级最高

引用值规范

代码语言:txt复制
# 获取入参请求头aaa的值
input.request.headers.aaa

# 获取入参请求体bbb字段的值
input.request.body.bbb

# 获取入参URL Query参数fff字段的值
input.request.params.fff

# 获取步骤1里request1的请求头ccc的值
step1.request1.request.headers.ccc

# 获取步骤1里request1的响应体ddd的值
step1.request1.response.body.ddd

# 获取步骤1结果里eee的值
step1.result.eee
  • 支持单值引用,如:string,int等
  • 支持对象类型的引用

input: 表示调用方的输入数据,如H5页面提交上来的参数

stepN.requestN: 表示步骤N里调用接口N的相关参数

stepN.result: 表示步骤N的转换结果

##动态脚本

目前支持Javascript和groovy两种脚本语言, 当有复杂的业务逻辑时可通过脚本来处理。

###Javascript

Javascript (推荐) - ECMAScript 5标准

JS脚本只支持单函数,且函数名不可变,在创建脚本时系统会自动生成初始模板,模板里包含相关使用说明

aggr_config_script_1.pngaggr_config_script_1.png

###Groovy

aggr_config_script_2.pngaggr_config_script_2.png

异常处理

当要在脚本里中止请求时可以通过以下方式来实现

aggr_config_exception.pngaggr_config_exception.png

返回一个对象且这个对象包含一个_stopAndResponse等于true的属性,Fizz会终止后续的操作并把这个对象返回给调用方。

#结束语

服务编排功能主要是通过配置的方式来取代原来微服务体系里的聚合类接口,减少胶水代码,通常情况下通过简单的数据映射和转换就可以完成一个聚合接口的开发工作。

介绍

作者:日尧

Fizz Gateway开源地址:https://github.com/wehotel/fizz-gateway-community

0 人点赞