美文网首页饥人谷技术博客
如何通过Fizz网关做服务编排

如何通过Fizz网关做服务编排

作者: linwaiwai | 来源:发表于2020-10-15 12:35 被阅读0次

    home: false
    title: 服务编排配置


    创建服务

    image.png

    创建聚合接口

    image.png image.png

    配置输入

    image.png
    • 配置输入的定义包括3部分:请求头、请求体和Query参数
    • 基于JSON Schema规范
    • 自带校验规则
    • 支持自定义脚本实现复杂的逻辑校验

    JSON Schema规范,详见:

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

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

    配置校验结果

    image.png
    • 校验不通过时,Fizz会把校验失败的原因(如:订单ID不能为空)放到上下文的validateMsg字段里
    • 可以自定义返回给调用方的报文格式,如 msgCode, message
    • 支持自定义响应头
    • 支持自定义脚本处理校验结果

    配置步骤

    配置步骤的基础信息

    image.png

    配置步骤的接口入出参

    image.png

    步骤说明

    • 一个聚合接口可包含多个步骤
    • 一个步骤可包含多个请求(即调用多个接口)
    • 步骤间是串联顺序执行
    • 一个步骤内的多个请求并行执行

    数据转换

    支持配置固定值,引用值和脚本

    固定值

    image.png

    引用值

    image.png

    脚本

    image.png image.png

    星号 *

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

    image.png

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

    优先级与覆盖顺序

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

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

    引用值规范

    # 获取入参请求头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的转换结果

    Fallback与预处理条件

    image.png

    Fallback:

    当调用接口发生异常(如超时、网络或系统异常)可配置fallback方案:

    • Stop: 终止请求并立即返回
    • Continue: 继续后续的操作,且要设置默认的fallback json

    预处理: 根据条件判断是否要调用接口,脚本返回true时才调用接口

    配置步骤结果处理

    image.png
    • 支持对步骤里调用的每一个接口的返回结果做数据转换,如果配置数据转换规则原样返回并存储到上下文里供后续使用

    • 支持对步骤里调用的一个或多个接口的返回结果做处理,并把处理完的结果存储到上下文里供后续使用,不配置则不处理

    配置输出

    image.png

    配置返回给调用方的结果

    • 支持配置响应头
    • 支持配置响应体
    • 支持自定脚本处理复杂的业务逻辑

    脚本

    目前支持以下脚本语言:

    Javascript (推荐) - ECMAScript 5标准

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

    image.png

    Groovy

    image.png

    common.js 提供了操作context上下文的便捷操作函数

    /**
     * context 上下文便捷操作函数
     *
     */
    var common = {
      /* *********** private function begin *********** */
    
      // 获取上下文中客户端请求对象
      getInputReq: function (ctx) {
        if (!ctx || !ctx['input'] || !ctx['input']['request']) {
          return {};
        }
        return ctx['input']['request']
      },
    
      // 获取上下文步骤中请求接口的请求对象
      getStepReq: function (ctx, stepName, requestName) {
        if (!ctx || !stepName || !requestName) {
          return {};
        }
        if (!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] ||
          !ctx[stepName]['requests'][requestName]['request']) {
          return {};
        }
        return ctx[stepName]['requests'][requestName]['request'];
      },
    
      // 获取上下文步骤中请求接口的响应对象
      getStepResp: function (ctx, stepName, requestName) {
        if (!ctx || !stepName || !requestName) {
          return {};
        }
        if (!ctx[stepName] || !ctx[stepName]['requests'] || !ctx[stepName]['requests'][requestName] ||
          !ctx[stepName]['requests'][requestName]['response']) {
          return {};
        }
        return ctx[stepName]['requests'][requestName]['response'];
      },
    
      /* *********** private function end *********** */
    
      /* *********** input begin ************ */
    
      /**
       * 获取客户端请求头
       * @param {*} ctx 上下文 【必填】
       * @param {*} headerName 请求头字段名 【选填】,不传时返回所有请求头
       */
      getInputReqHeader: function (ctx, headerName) {
        var req = this.getInputReq(ctx);
        var headers = req['headers'] || {};
        return headerName ? headers[headerName] : headers;
      },
    
      /**
       * 获取客户端URL请求参数(query string)
       * @param {*} ctx 上下文 【必填】
       * @param {*} paramName URL参数名 【选填】,不传时返回所有请求参数
       */
      getInputReqParam: function (ctx, paramName) {
        var req = this.getInputReq(ctx);
        var params = req['params'] || {};
        return paramName ? params[paramName] : params;
      },
    
      /**
       * 获取客户端请求体
       * @param {*} ctx 上下文 【必填】
       * @param {*} field 字段名 【选填】,不传时返回整个请求体
       */
      getInputReqBody: function (ctx, field) {
        var req = this.getInputReq(ctx);
        var body = req['body'] || {};
        return field ? body[field] : body;
      },
    
      /**
       * 获取返回给客户端的响应头
       * @param {*} ctx 上下文 【必填】
       * @param {*} headerName 响应头字段名 【选填】,不传时返回所有响应头
       */
      getInputRespHeader: function (ctx, headerName) {
        var req = this.getInputReq(ctx);
        var headers = req['headers'] || {};
        return headerName ? headers[headerName] : headers;
      },
    
      /**
       * 获取返回给客户端的响应体
       * @param {*} ctx 上下文 【必填】
       * @param {*} field 字段名 【选填】,不传时返回整个响应体
       */
      getInputRespBody: function (ctx, field) {
        var req = this.getInputReq(ctx);
        var body = req['body'] || {};
        return field ? body[field] : body;
      },
    
      /* *********** input begin ************ */
    
      /* *********** step request begin ************ */
    
      /**
       * 获取步骤中调用的接口的请求头
       * @param {*} ctx 上下文 【必填】
       * @param {*} stepName 步骤名【必填】
       * @param {*} requestName 请求的接口名 【必填】
       * @param {*} headerName 请求头字段名 【选填】,不传时返回所有请求头
       */
      getStepReqHeader: function (ctx, stepName, requestName, headerName) {
        var req = this.getStepReq(ctx, stepName, requestName);
        var headers = req['headers'] || {};
        return headerName ? headers[headerName] : headers;
      },
    
      /**
       * 获取步骤中调用的接口的URL参数
       * @param {*} ctx 上下文 【必填】
       * @param {*} stepName 步骤名【必填】
       * @param {*} requestName 请求的接口名 【必填】
       * @param {*} paramName URL参数名 【选填】,不传时返回所有URL参数
       */
      getStepReqParam: function (ctx, stepName, requestName, paramName) {
        var req = this.getStepReq(ctx, stepName, requestName);
        var params = req['params'] || {};
        return paramName ? params[paramName] : params;
      },
    
      /**
       * 获取步骤中调用的接口的请求体
       * @param {*} ctx 上下文 【必填】
       * @param {*} stepName 步骤名【必填】
       * @param {*} requestName 请求的接口名 【必填】
       * @param {*} field 字段名 【选填】,不传时返回整个请求体
       */
      getStepReqBody: function (ctx, stepName, requestName, field) {
        var req = this.getStepReq(ctx, stepName, requestName);
        var body = req['body'] || {};
        return field ? body[field] : body;
      },
    
      /**
       * 获取步骤中调用的接口的响应头
       * @param {*} ctx 上下文 【必填】
       * @param {*} stepName 步骤名【必填】
       * @param {*} requestName 请求的接口名 【必填】
       * @param {*} headerName 响应头字段名 【选填】,不传时返回所有响应头
       */
      getStepRespHeader: function (ctx, stepName, requestName, headerName) {
        var resp = this.getStepResp(ctx, stepName, requestName);
        var headers = resp['headers'] || {};
        return headerName ? headers[headerName] : headers;
      },
    
      /**
       * 获取步骤中调用的接口的响应头
       * @param {*} ctx 上下文 【必填】
       * @param {*} stepName 步骤名【必填】
       * @param {*} requestName 请求的接口名 【必填】
       * @param {*} field 字段名 【选填】,不传时返回整个响应头
       */
      getStepRespBody: function (ctx, stepName, requestName, field) {
        var resp = this.getStepResp(ctx, stepName, requestName);
        var body = resp['body'] || {};
        return field ? body[field] : body;
      },
    
      /**
       * 获取步骤结果
       * @param {*} ctx 上下文 【必填】
       * @param {*} stepName 步骤名【必填】
       * @param {*} field 字段名 【选填】,不传时返回整个步骤结果对象
       */
      getStepResult: function (ctx, stepName, field) {
        if (!ctx || !stepName || !ctx[stepName]) {
          return {};
        }
        var result = ctx[stepName]['result'] || {};
        return field ? result[field] : result;
      }
    
      /* *********** step request end ************ */
    
    };
    
    

    context.js 数据结构

    
    // 上下文,用于保存客户输入输出和每个步骤的输入与输出结果
    var context = {
        // 是否DEBUG模式
        debug:false,
    
        // 各个操作的耗时
        elapsedTimes: [{
            [actionName]: 123, // 操作名称:耗时
        }],
    
      // 客户输入和接口的返回结果
      input: {
          request:{
            path: "",
              method: "GET/POST",
              headers: {},
              body: {},
              params: {}
          },
          response: { // 聚合接口的响应
              headers: {},
              body: {}
          }
      },
    
      // 步骤
      step1: {
          requests: {
            // 接口1
              request1: {
                // 请求相关参数
                  request:{
                      url: "",
                      method: "GET/POST",
                      headers: {},
                      body: {}
                  },
                  // 根据转换规则转换后的接口响应
                  response: {
                      headers: {},
                      body: {}
                  }
              },
              // 接口2
              request2: {
                  request:{
                      url: "",
                      method: "GET/POST",
                      headers: {},
                      body: {}
                  },
                  response: {
                  headers: {},
                      body: {}
                  }
              }
              //...
          },
    
          // 步骤结果
          result: {}
    
      }
    };
    
    

    异常处理

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

    image.png

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

    在线测试

    image.png
    • 支持在线实时测试
    • 支持测试接口和正式接口隔离
    • 支持返回上下文,可以查看整个执行过程中各个步骤及请求的输入与输出
    • 支持保存历史测试记录
    image.png

    支持调试模式,在测试接口和正式接口均可使用,修改后重新发布可实时生效,在调试模式下会打印请求日志及报文,主要用于排查线上问题

    导入导出

    image.png

    导入导出主要用于在各个环境间同步接口配置,在开发环境配置好后导到测试环境中测试,测试完后导到生产环境进行发布

    发布|下线和审核

    image.png

    目前发布|下线申请有以上两个入口。

    image.png image.png
    • 批量发布:对发布单里的接口进行批量发布
    • 批量回滚:对发布单里的接口进行批量回滚
    • 发布:实时发布到网关
    • 回滚:支持回滚到历史任何一个版本,可在发布历史里指定一个版本进行回滚
    • 下线:从网关删除接口,在后台可以通过发布功能再次上线

    发布流程说明

    申请发布、审核、发布和下线功能的权限可根据需要灵活分配给不同角色,如:开发人员只能申请发布,上级领导审核,运维或测试人员执行发布、回滚或下线。在开发、测试和预生产环境为了方便开发人员调试也可把申请发布、审核、发布和下线功能都分配给开发人员。

    相关文章

      网友评论

        本文标题:如何通过Fizz网关做服务编排

        本文链接:https://www.haomeiwen.com/subject/qdinpktx.html