如何通過Fizz網關做服務編排


home: false
title: 服務編排配置


創建服務

創建聚合接口

配置輸入

  • 配置輸入的定義包括3部分:請求頭、請求體和Query參數
  • 基於JSON Schema規範
  • 自帶校驗規則
  • 支持自定義腳本實現複雜的邏輯校驗

JSON Schema規範,詳見:

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

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

配置校驗結果

  • 校驗不通過時,Fizz會把校驗失敗的原因(如:訂單ID不能爲空)放到上下文的validateMsg字段裏
  • 可以自定義返回給調用方的報文格式,如 msgCode, message
  • 支持自定義響應頭
  • 支持自定義腳本處理校驗結果

配置步驟

配置步驟的基礎信息

配置步驟的接口入出參

步驟說明

  • 一個聚合接口可包含多個步驟
  • 一個步驟可包含多個請求(即調用多個接口)
  • 步驟間是串聯順序執行
  • 一個步驟內的多個請求並行執行

數據轉換

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

固定值

引用值

腳本

星號 *

星號通配符可以接收一個返回對象類型的引用值,返回對象裏的字段會合併到目標對象裏

樣例: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與預處理條件

Fallback:

當調用接口發生異常(如超時、網絡或系統異常)可配置fallback方案:

  • Stop: 終止請求並立即返回
  • Continue: 繼續後續的操作,且要設置默認的fallback json

預處理: 根據條件判斷是否要調用接口,腳本返回true時才調用接口

配置步驟結果處理

  • 支持對步驟裏調用的每一個接口的返回結果做數據轉換,如果配置數據轉換規則原樣返回並存儲到上下文裏供後續使用

  • 支持對步驟裏調用的一個或多個接口的返回結果做處理,並把處理完的結果存儲到上下文裏供後續使用,不配置則不處理

配置輸出

配置返回給調用方的結果

  • 支持配置響應頭
  • 支持配置響應體
  • 支持自定腳本處理複雜的業務邏輯

腳本

目前支持以下腳本語言:

Javascript (推薦) - ECMAScript 5標準

JS腳本只支持單函數,且函數名不可變,在創建腳本時系統會自動生成初始模板,模板裏包含相關使用說明

Groovy

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: {}

  }
};

異常處理

當要在腳本里中止請求時可以通過以下方式來實現

返回一個對象且這個對象包含一個_stopAndResponse等於true的屬性,Fizz會終止後續的操作並把這個對象返回給調用方。

在線測試

  • 支持在線實時測試
  • 支持測試接口和正式接口隔離
  • 支持返回上下文,可以查看整個執行過程中各個步驟及請求的輸入與輸出
  • 支持保存歷史測試記錄

支持調試模式,在測試接口和正式接口均可使用,修改後重新發布可實時生效,在調試模式下會打印請求日誌及報文,主要用於排查線上問題

導入導出

導入導出主要用於在各個環境間同步接口配置,在開發環境配置好後導到測試環境中測試,測試完後導到生產環境進行發佈

發佈|下線和審覈

目前發佈|下線申請有以上兩個入口。

  • 批量發佈:對發佈單裏的接口進行批量發佈
  • 批量回滾:對發佈單裏的接口進行批量回滾
  • 發佈:實時發佈到網關
  • 回滾:支持回滾到歷史任何一個版本,可在發佈歷史裏指定一個版本進行回滾
  • 下線:從網關刪除接口,在後臺可以通過發佈功能再次上線

發佈流程說明

申請發佈、審覈、發佈和下線功能的權限可根據需要靈活分配給不同角色,如:開發人員只能申請發佈,上級領導審覈,運維或測試人員執行發佈、回滾或下線。在開發、測試和預生產環境爲了方便開發人員調試也可把申請發佈、審覈、發佈和下線功能都分配給開發人員。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章