溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Serverless怎么將配置化思想復(fù)用到平臺系統(tǒng)中

發(fā)布時間:2021-12-16 12:00:07 來源:億速云 閱讀:172 作者:柒染 欄目:云計算

這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Serverless怎么將配置化思想復(fù)用到平臺系統(tǒng)中,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

什么是 Serverless

Serverless 由兩部分組成,Server 和 Less。

  • 前者可以理解為其解決方案范圍處在服務(wù)端;

  • 后者可以譯為少量的;

組合起來就是較少服務(wù)端干預(yù)的服務(wù)端解決方案。

與 Serverless 相對的是 Serverfull,比較下對應(yīng)的概念可能更便于理解。

Serverfull 時代,研發(fā)交付流程一般有三個角色:RD,PM,QA。

RD 根據(jù) PM 的 PRD 進(jìn)行功能開發(fā),交付到 QA 進(jìn)行測試,測試完成之后發(fā)布到服務(wù)器。由運維人員規(guī)劃服務(wù)器規(guī)格、數(shù)量、機房部署、節(jié)點擴縮容等,這種更多由人力處理的時代就是 Serverfull 時代。

之后進(jìn)入了 DevOps 時代。這個時代運維自己開發(fā)一套運維控制臺,可以讓研發(fā)同學(xué)在控制臺上自己進(jìn)行服務(wù)觀測、數(shù)據(jù)查詢、運維處理等,運維同學(xué)的工作輕松了不少,這個階段主要釋放了運維同學(xué)的人力。

而到了 Serverless 時代,這套運維控制臺能力越來越豐富,可以實現(xiàn)按配置的自動擴縮容、性能監(jiān)控、DevOps 流水線等,同時侵入到研發(fā)流程側(cè),比如自動發(fā)布流水線、編譯打包、代碼質(zhì)量監(jiān)測、灰度發(fā)布、彈性擴縮等流程基本不需要人力處理了,這就是 Serverless 時代。

Serverless 怎么用

相信你有過這樣的經(jīng)歷,在一個 Web 界面上,左側(cè)寫代碼,右側(cè)展示執(zhí)行效果。

  • 寫的是代碼塊,代碼數(shù)量不會特別大;

  • 代碼運行速度快;

  • 支持多種編程語言;

  • 可以支持不可預(yù)計的流量洪峰沖擊。

抽象來說,前端只需要將代碼片段和編程語言的標(biāo)識傳給 Server 端即可,等待響應(yīng)結(jié)果。Server 端可以針對于不同編程語言進(jìn)行 runtime 分類、預(yù)處理等工作。

Serverless 怎么做

很多人把 Serverless 看做是 FC(function compute:函數(shù)計算),使用函數(shù)計算,無需業(yè)務(wù)自己搭建 IT 基礎(chǔ)設(shè)施,只需要編碼并上傳代碼。函數(shù)計算會按需為你準(zhǔn)備好計算資源,彈性、可靠地運行,并提供 trace、日志查詢、監(jiān)控告警等治理能力。

在 FC 中有服務(wù)和函數(shù)之分。一個服務(wù)可以包含多個函數(shù)。我們可以用微服務(wù)理解,我們通過 golang 或 java 搭建了一個微服務(wù)架構(gòu),而 FC 服務(wù)就是其中的類,F(xiàn)C 函數(shù)是類中的一個方法。

區(qū)別在于 Java 搭建的微服務(wù)只能運行 java 類代碼,golang 的類只能運行 go 寫的代碼,而 FC 函數(shù)可以安裝不同語言的 runtime,支持運行不同語言程序。

類比理解之后,我們再看下如何調(diào)用 FC 的函數(shù),一般的 FC 解決方案里面都有一個觸發(fā)器的概念。比如 HTTP 觸發(fā)器、對象存儲觸發(fā)器、日志服務(wù)觸發(fā)器、定時任務(wù)觸發(fā)器、CDN 觸發(fā)器、消息隊列觸發(fā)器等。觸發(fā)器是對于 FC 函數(shù)調(diào)用的抽象收口,比如 HTTP 觸發(fā)器一般都類比網(wǎng)關(guān)的一個 http 請求事件,或是指定對象存儲路徑下上傳了一個圖片,這些觸發(fā)事件的入口都可以是觸發(fā)器。

觸發(fā)器產(chǎn)生事件之后可以調(diào)用 FC 函數(shù),函數(shù)執(zhí)行的邏輯可以是下載一張圖片或是注冊一個用戶。

這樣從觸發(fā)器到 FC 函數(shù)邏輯處理就是一個 FC 的生命周期了。

那么 FC 是如何實現(xiàn)高可用的呢?

其實每個函數(shù)底層代碼都是運行在一套 IaaS 平臺上,使用 IaaS 資源,我們可以為每個函數(shù)設(shè)置運行代碼時需要的內(nèi)存配置即可,比如最小 128M,最大 3G 等。研發(fā)人員不需要關(guān)心代碼運行在什么樣的服務(wù)器上,不需要關(guān)心啟動了多少函數(shù)實例支持當(dāng)前場景,不需要關(guān)注背后的彈性擴縮問題,這些都被收斂在 FC 之后。

有兩種高可用策略:

  • 給函數(shù)設(shè)置并發(fā)實例數(shù),比如 3 個,那么當(dāng)有三個請求進(jìn)來時,該函數(shù)只啟動一個實例,但是會啟動三個線程來運行邏輯;

  • 線程達(dá)到上限后,會再拉起一個函數(shù)實例。

那么 Serverless 如何提效呢?

  • 效率高:如果新加了語言,只需要創(chuàng)建一個對應(yīng)的 Runtime 的 FC 函數(shù)即可;

  • 高可用:通過多線程、多實例兩種方式保障高可用,且函數(shù)實例擴縮容完全由 FC 自助處理,不需要運維做任何配置;

  • 成本低:在沒有觸發(fā)器請求時,函數(shù)實例不會被拉起,也不會計費,所以在流量低谷期間或者夜間時,F(xiàn)C 消耗的成本是非常低的。

如何在云平臺創(chuàng)建一個 FC

1. 創(chuàng)建服務(wù)

  • 首先新建一個服務(wù)名稱;

  • 選定服務(wù)部署的地區(qū)(背后幫助你就近部署在目標(biāo)機房);

  • 選擇是否打開調(diào)試日志(開發(fā)過程開啟,線上運行時可關(guān)閉)。

2. 創(chuàng)建函數(shù)

有了服務(wù)之后就可以創(chuàng)建函數(shù)了,比如選擇基于 http 請求的函數(shù)。

  • 選擇函數(shù)綁定的服務(wù);

  • 設(shè)置函數(shù)名稱;

  • 選擇 runtime 環(huán)境;

  • 是否要求函數(shù)實例彈性;

  • 函數(shù)入口(觸發(fā)器直接調(diào)用的目標(biāo)方法);

  • 函數(shù)執(zhí)行內(nèi)存;

  • 函數(shù)執(zhí)行超時時間;

  • 設(shè)置實例并發(fā)度。

配置觸發(fā)器,比如選擇了 HTTP 觸發(fā)器,然后在觸發(fā)器上綁定函數(shù)名稱,由于是 http 訪問,可以選擇訪問的鑒權(quán)、認(rèn)證方式,以及請求方式 POST or GET。

3. 代碼編寫

當(dāng)函數(shù)創(chuàng)建好了之后,進(jìn)入函數(shù),可以看到描述、代碼執(zhí)行歷史、觸發(fā)器類型、日志查詢頁等。
如果是 HTTP 觸發(fā)器,需要配置 http 觸發(fā)路徑。

類似于類里面的一個函數(shù),上下文請求會打到這里,直接執(zhí)行。

Python 代碼為例:

# -*- coding: utf-8 -*-
import logging
import urllib.parse
import time
import subprocess
def handler(environ, start_response):
    context = environ['fc.context']
    request_uri = environ['fc.request_uri']
    for k, v in environ.items():
      if k.startswith('HTTP_'):
        pass
    try:        
        request_body_size = int(environ.get('CONTENT_LENGTH', 0))    
    except (ValueError):        
        request_body_size = 0   
    # 獲取用戶傳入的code
    request_body = environ['wsgi.input'].read(request_body_size)  
    codeStr = urllib.parse.unquote(request_body.decode("GBK"))
    # 因為body里的對象里有code和input兩個屬性,這里分別獲取用戶code和用戶輸入
    codeArr = codeStr.split('&')
    code = codeArr[0][5:]
    inputStr = codeArr[1][6:]
    # 將用戶code保存為py文件,放/tmp目錄下,以時間戳為文件名
    fileName = '/tmp/' + str(int(time.time())) + '.py'
    f = open(fileName, "w")
    # 這里預(yù)置引入了time庫
    f.write('import time \r\n')
    f = open(fileName, "a")
    f.write(code)
    f.close()
    # 創(chuàng)建子進(jìn)程,執(zhí)行剛才保存的用戶code py文件
    p = subprocess.Popen("python ">

流程如下:

  • 前端傳入代碼片段,格式是字符串;

  • 在 FC 函數(shù)中獲取到傳入的代碼字符串,截取 code 內(nèi)容和 input 內(nèi)容;

  • 將代碼保存為一個 py 文件,以時間戳為文件命名,保存在 FC 函數(shù)的 /tmp 目錄下,每個函數(shù)有自己獨立的 /tmp 目錄;

  • import time 庫代碼;

  • 通過 subprocess 創(chuàng)建子流程,以 shell 方式通過 py 命令執(zhí)行保存在 /tmp 目錄下的 py 文件;

  • 最后讀取執(zhí)行結(jié)果返回給前端。

整個過程只需要前端將代碼傳入到 FC 函數(shù)里面,整個 Server 端各個環(huán)節(jié)都不需要研發(fā)與運維同學(xué)關(guān)心,體現(xiàn)了 Serverless 的精髓。

用 Serverless 協(xié)調(diào)工作流

工作流可以用順序、分支、并行等方式來編排任務(wù)執(zhí)行,之后流程會按照設(shè)定好的步驟可靠地協(xié)調(diào)任務(wù)執(zhí)行,跟蹤每個任務(wù)的狀態(tài)切換,并在必要時執(zhí)行定義的重試邏輯,確保流程順利執(zhí)行。

工作流流程通過記錄日志和審計方式來監(jiān)視工作流的執(zhí)行,便于流程的診斷與調(diào)試。

系統(tǒng)靈活性與擴展性的核心是服務(wù)可編排,所以我們需要做的是將現(xiàn)有系統(tǒng)內(nèi)部用戶希望定制的功能進(jìn)行梳理、拆分、抽離、結(jié)合 FC 提供的無狀態(tài)能力,將這些功能點進(jìn)行編排,實現(xiàn)業(yè)務(wù)流程的定制。

需靈活配置工作流的業(yè)務(wù)

舉個例子,比如餐飲場景下不同商家可以配置不同的支付方式,可以走微信支付、銀聯(lián)支付、支付寶支付??梢酝瑫r支持三家,也可以某一家,可以到付,也可以積分兌換等。如果沒有一個好的配置化流程解決方案的話,系統(tǒng)中會出現(xiàn)大量硬編碼規(guī)則判斷條件,系統(tǒng)迭代疲于奔命,是個不可持續(xù)的過程。

有了 FC 搭建的工作流就可以很優(yōu)雅地解決這種問題。

上面的流程是用戶側(cè)的流程,接下來需要轉(zhuǎn)換成程序側(cè)的流程,通過約束的 FDL 創(chuàng)建工作流。

FDL 代碼如下:

version: v1beta1
type: flow
timeoutSeconds: 3600
steps:
  - type: task
    name: generateInfo
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: products
        source: $input.products
      - target: supplier
        source: $input.supplier
      - target: address
        source: $input.address
      - target: orderNum
        source: $input.orderNum
      - target: type
        source: $context.step.name
    outputMappings:
      - target: paymentcombination
        source: $local.paymentcombination
      - target: orderNum
        source: $local.orderNum
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
  -type: task
    name: payment
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: orderNum
        source: $local.orderNum
      - target: paymentcombination
        source: $local.paymentcombination
      - target: type
        source: $context.step.name
    outputMappings:
      - target: paymentMethod
        source: $local.paymentMethod
      - target: orderNum
        source: $local.orderNum
      - target: price
        source: $local.price
      - target: taskToken
        source: $input.taskToken
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
  - type: choice
    name: paymentCombination
    inputMappings:
      - target: orderNum
        source: $local.orderNum
      - target: paymentMethod
        source: $local.paymentMethod
      - target: price
        source: $local.price
      - target: taskToken
        source: $local.taskToken
    choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price            
              - target: orderNum
                source: $input.orderNum
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken
      - condition: $.paymentMethod == "weixin"
        steps:
          - type: task
            name: weixin
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
      - condition: $.paymentMethod == "unionpay"
        steps:
          - type: task
            name: unionpay
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
    default:
      goto: orderCanceled
  - type: task
    name: orderCompleted
    resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/orderCompleted
    end: true
  - type: task
    name: orderCanceled
    resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/cancerOrder

示例體現(xiàn)了基于 Serverless 的 FC 可實現(xiàn)靈活工作流。

流程如何觸發(fā)的呢?

在用戶選擇完商品、填完地址之后,通過拉取商品、訂單上下文,可以自動化觸發(fā)流程了。

在微服務(wù)背景下,很多能力不是閉環(huán)在單體代碼邏輯之內(nèi),很多時候是多個業(yè)務(wù)系統(tǒng)的連接,比如串聯(lián)多個 OpenAPI 接口實現(xiàn)。

如想使用流程引擎需要進(jìn)行相關(guān)的備案鑒權(quán):

@Configuration
public class FNFConfig {
    @Bean
    public IAcsClient createDefaultAcsClient(){
        DefaultProfile profile = DefaultProfile.getProfile(
                "cn-xxx",          // 地域ID
                "ak",      // RAM 賬號的AccessKey ID
                "sk"); // RAM 賬號Access Key Secret
        IAcsClient client = new DefaultAcsClient(profile);
        return client;
    }
}

startFNF 代碼里面流程如何串聯(lián)起來:

  • 輸入要啟動的流程名稱,比如每次訂單編號作為啟動流程實例名稱;

  • 流程啟動后的流程實例名稱;

  • 啟動輸入?yún)?shù),比如業(yè)務(wù)參數(shù),比如一個 json 里面有商品、商家、地址、訂單等上下文信息。

@GetMapping("/startFNF/{fnfname}/{execuname}/{input}")
    public StartExecutionResponse startFNF(@PathVariable("fnfname") String fnfName,
                                           @PathVariable("execuname") String execuName,
                                           @PathVariable("input") String inputStr) throws ClientException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("fnfname", fnfName);
        jsonObject.put("execuname", execuName);
        jsonObject.put("input", inputStr);
        return fnfService.startFNF(jsonObject);
    }

再看下 fnfService.startFNF:

@Override
    public StartExecutionResponse startFNF(JSONObject jsonObject) throws ClientException {
        StartExecutionRequest request = new StartExecutionRequest();
        String orderNum = jsonObject.getString("execuname");
        request.setFlowName(jsonObject.getString("fnfname"));
        request.setExecutionName(orderNum);
        request.setInput(jsonObject.getString("input"));
        JSONObject inputObj = jsonObject.getJSONObject("input");
        Order order = new Order();
        order.setOrderNum(orderNum);
        order.setAddress(inputObj.getString("address"));
        order.setProducts(inputObj.getString("products"));
        order.setSupplier(inputObj.getString("supplier"));
        orderMap.put(orderNum, order);
        return iAcsClient.getAcsResponse(request);
    }
  • 第一部分是啟動流程;

  • 第二部分是創(chuàng)建訂單對下,并模擬入庫。

前端如何調(diào)用?

在前端當(dāng)點擊選擇商品和商家頁面中的下一步后,通過 GET 方式調(diào)用 HTTP 協(xié)議的接口 /startFNF/{fnfname}/{execuname}/{input}。和上面的 Java 方法對應(yīng)。

  • fnfname:要啟動的流程名稱;

  • execuname:隨機生成 uuid,作為訂單的編號,也作為啟動流程實例的名稱;

  • input:將商品、商家、訂單號、地址構(gòu)建為 JSON 字符串傳入流程。

submitOrder(){
                const orderNum = uuid.v1()
                this.$axios.$get('/startFNF/OrderDemo-Jiyuan/'+orderNum+'/{\n' +
                    '  "products": "'+this.products+'",\n' +
                    '  "supplier": "'+this.supplier+'",\n' +
                    '  "orderNum": "'+orderNum+'",\n' +
                    '  "address": "'+this.address+'"\n' +
                    '}' ).then((response) => {
                    console.log(response)
                    if(response.message == "success"){
                        this.$router.push('/orderdemo/' + orderNum)
                    }
                })
            }

1. generateInfo 節(jié)點

先看下第一個 FDL 節(jié)點定義:

- type: task
    name: generateInfo
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: products
        source: $input.products
      - target: supplier
        source: $input.supplier
      - target: address
        source: $input.address
      - target: orderNum
        source: $input.orderNum
      - target: type
        source: $context.step.name
    outputMappings:
      - target: paymentcombination
        source: $local.paymentcombination
      - target: orderNum
        source: $local.orderNum
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
```


- name:節(jié)點名稱;
- timeoutSeconds:超時時間,節(jié)點等待時長,超過時間后跳轉(zhuǎn)到 goto 分支指向的 orderCanceled 節(jié)點;
- pattern:設(shè)置為 waitForCallback,表示需要等待確認(rèn);
- inputMappings:該節(jié)點入?yún)ⅲ?
   - taskToken:Serverless 工作流自動生成的 Token;
   - products:選擇的商品;
   - supplier:選擇的商家;
   - address:送餐地址;
   - orderNum:訂單號;
- outputMappings:該節(jié)點的出參;
   - paymentcombination:該商家支持的支付方式;
   - orderNum:訂單號;
- catch:捕獲異常,跳轉(zhuǎn)到其他分支。


Serverless 工作流支持多個云服務(wù)集成,將其他服務(wù)作為任務(wù)步驟的執(zhí)行單元。服務(wù)集成方式通過 FDL 表達(dá)式實現(xiàn),在任務(wù)步驟中,可以使 用resourceArn 來定義集成的目標(biāo)服務(wù),使用 pattern 定義集成模式。


在 resourceArn 中配置 /topics/generateInfo-fnf-demo-jiyuan/messages 信息,就是集成了 MNS 消息隊列服務(wù),當(dāng) generateInfo 節(jié)點觸發(fā)后會向 generateInfo-fnf-demo-jiyuanTopic 中發(fā)送一條消息。消息的正文和參數(shù)在 serviceParams 對象中 zhi'd 指定。MessageBody 是消息正文,配置 $ 表示通過輸入映射 inputMappings 產(chǎn)生消息正文。

generateInfo-fnf-demo 函數(shù):

向 generateInfo-fnf-demo-jiyuanTopic 中發(fā)送的這條消息包含了商品信息、商家信息、地址、訂單號,表示一個下訂單流程的開始,既然有發(fā)消息,那么必然有接受消息進(jìn)行后續(xù)處理。在函數(shù)計算控制臺,創(chuàng)建服務(wù),在服務(wù)下創(chuàng)建名為 generateInfo-fnf-demo 的事件觸發(fā)器函數(shù),這里選擇 Python Runtime:

![17.png](https://ucc.alicdn.com/pic/developer-ecology/89d43991349e422e8bda26f905cce0c4.png)

創(chuàng)建 MNS 觸發(fā)器,選擇監(jiān)聽 generateInfo-fnf-demo-jiyuanTopic:

![18.png](https://ucc.alicdn.com/pic/developer-ecology/4612a6cd4ba54dc38a79cebf7cacc3ac.png)

打開消息服務(wù) MNS 控制臺,創(chuàng)建 generateInfo-fnf-demo-jiyuanTopic:

![19.png](https://ucc.alicdn.com/pic/developer-ecology/f51714a2ee594dcba4bbbc888c42288c.png)

接下來寫函數(shù)代碼:
-- coding: utf-8 --
import logging
import json
import time
import requests
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest
from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest
def handler(event, context):

1. 構(gòu)建Serverless工作流Client

region = "cn-hangzhou"
account_id = "XXXX"
ak_id = "XXX"
ak_secret = "XXX"
fnf_client = AcsClient(
    ak_id,
    ak_secret,
    region
)
logger = logging.getLogger()
# 2. event內(nèi)的信息即接受到Topic generateInfo-fnf-demo-jiyuan中的消息內(nèi)容,將其轉(zhuǎn)換為Json對象
bodyJson = json.loads(event)
logger.info("products:">
代碼分五部分:

- 構(gòu)建 Serverless 工作流 Client;

- event 內(nèi)的信息即接受到 TopicgenerateInfo-fnf-demo-jiyuan 中的消息內(nèi)容,將其轉(zhuǎn)換為 Json 對象;

- 判斷什么商家使用什么樣的支付方式組合,這里的示例比較簡單粗暴,正常情況下,應(yīng)該使用元數(shù)據(jù)配置的方式獲取。比如在系統(tǒng)內(nèi)有商家信息的配置功能,通過在界面上配置該商家支持哪些支付方式,形成元數(shù)據(jù)配置信息,提供查詢接口,在這里進(jìn)行查詢;

- 調(diào)用 Java 服務(wù)暴露的接口,更新訂單信息,主要是更新支付方式;

- 給予 generateInfo 節(jié)點響應(yīng),并返回數(shù)據(jù),這里返回了訂單號和支付方式。因為該節(jié)點的 pattern 是 waitForCallback,所以需要等待響應(yīng)結(jié)果。

generateInfo-fnf-demo 函數(shù)配置了 MNS 觸發(fā)器,當(dāng) TopicgenerateInfo-fnf-demo-jiyuan 有消息后就會觸發(fā)執(zhí)行 generateInfo-fnf-demo 函數(shù)。

### 2. payment 節(jié)點

接下來是 payment 的 FDL 代碼定義:
  • type: task
    name: payment
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:

    • FnF.TaskTimeout
      goto: orderCanceled

    • target: taskToken
      source: $context.task.token

    • target: orderNum
      source: $local.orderNum - target: paymentcombination source: $local.paymentcombination

    • target: type
      source: $context.step.name outputMappings: - target: paymentMethod source: $local.paymentMethod

    • target: orderNum
      source: $local.orderNum - target: price source: $local.price

    • target: taskToken
      source: $input.taskToken serviceParams: MessageBody: $
      Priority: 1
      catch:

    • errors:

當(dāng)流程流轉(zhuǎn)到 payment 節(jié)點后,用戶就可以進(jìn)入到支付頁面。

payment 節(jié)點會向 MNS 的 Topicpayment-fnf-demo-jiyuan 發(fā)送消息,會觸發(fā) payment-fnf-demo 函數(shù)。

payment-fnf-demo 函數(shù):

payment-fnf-demo 函數(shù)的創(chuàng)建方式和 generateInfo-fnf-demo 函數(shù)類似。

# -*- coding: utf-8 -*-
import logging
import json
import os
import time
import logging
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkcore.client import AcsClient
from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest
from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest
from mns.account import Account  # pip install aliyun-mns
from mns.queue import *
def handler(event, context):
    logger = logging.getLogger()
    region = "xxx"
    account_id = "xxx"
    ak_id = "xxx"
    ak_secret = "xxx"
    mns_endpoint = "http://your_account_id.mns.cn-hangzhou.aliyuncs.com/"
    queue_name = "payment-queue-fnf-demo"
    my_account = Account(mns_endpoint, ak_id, ak_secret)
    my_queue = my_account.get_queue(queue_name)
    # my_queue.set_encoding(False)
    fnf_client = AcsClient(
        ak_id,
        ak_secret,
        region
    )
    eventJson = json.loads(event)
    isLoop = True
    while isLoop:
        try:
            recv_msg = my_queue.receive_message(30)
            isLoop = False
            # body = json.loads(recv_msg.message_body)
            logger.info("recv_msg.message_body:======================">

上面代碼核心思路是等待用戶在支付頁面選擇某個支付方式確認(rèn)支付。使用了 MNS 的隊列來模擬等待。循環(huán)等待接收隊列 payment-queue-fnf-demo 中的消息,當(dāng)收到消息后將訂單號和用戶選擇的具體支付方式以及金額返回給 payment 節(jié)點。

前端選擇支付方式頁面:

經(jīng)過 generateInfo 節(jié)點后,該訂單的支付方式信息已經(jīng)有了,所以對于用戶而言,當(dāng)填完商品、商家、地址后,跳轉(zhuǎn)到的頁面就是該確認(rèn)支付頁面,并且包含了該商家支持的支付方式。

進(jìn)入該頁面后,會請求 Java 服務(wù)暴露的接口,獲取訂單信息,根據(jù)支付方式在頁面上顯示不同的支付方式。

當(dāng)用戶選定某個支付方式點擊提交訂單按鈕后,向 payment-queue-fnf-demo 隊列發(fā)送消息,即通知 payment-fnf-demo 函數(shù)繼續(xù)后續(xù)的邏輯。

使用了一個 HTTP 觸發(fā)器類型的函數(shù),用于實現(xiàn)向 MNS 發(fā)消息的邏輯,paymentMethod-fnf-demo 函數(shù)代碼:

# -*- coding: utf-8 -*-
import logging
import urllib.parse
import json
from mns.account import Account  # pip install aliyun-mns
from mns.queue import *
HELLO_WORLD = b'Hello world!\n'
def handler(environ, start_response):
    logger = logging.getLogger() 
    context = environ['fc.context']
    request_uri = environ['fc.request_uri']
    for k, v in environ.items():
      if k.startswith('HTTP_'):
        # process custom request headers
        pass
    try:       
        request_body_size = int(environ.get('CONTENT_LENGTH', 0))   
    except (ValueError):       
        request_body_size = 0  
    request_body = environ['wsgi.input'].read(request_body_size) 
    paymentMethod = urllib.parse.unquote(request_body.decode("GBK"))
    logger.info(paymentMethod)
    paymentMethodJson = json.loads(paymentMethod)
    region = "cn-xxx"
    account_id = "xxx"
    ak_id = "xxx"
    ak_secret = "xxx"
    mns_endpoint = "http://your_account_id.mns.cn-hangzhou.aliyuncs.com/"
    queue_name = "payment-queue-fnf-demo"
    my_account = Account(mns_endpoint, ak_id, ak_secret)
    my_queue = my_account.get_queue(queue_name)
    output = "{\"paymentMethod\": \"%s\", \"price\":\"%s\">

函數(shù)的邏輯很簡單,就是向 MNS 的隊列 payment-queue-fnf-demo 發(fā)送用戶選擇的支付方式和金額。

3. paymentCombination 節(jié)點

paymentCombination 節(jié)點是一個路由節(jié)點,通過判斷某個參數(shù)路由到不同的節(jié)點,以 paymentMethod 作為判斷條件:

- type: choice
    name: paymentCombination
    inputMappings:
      - target: orderNum
        source: $local.orderNum
      - target: paymentMethod
        source: $local.paymentMethod
      - target: price
        source: $local.price
      - target: taskToken
        source: $local.taskToken
    choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price            
              - target: orderNum
                source: $input.orderNum
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken
      - condition: $.paymentMethod == "weixin"
        steps:
          - type: task
            name: weixin
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
      - condition: $.paymentMethod == "unionpay"
        steps:
          - type: task
            name: unionpay
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
    default:
      goto: orderCanceled

流程是,用戶選擇支付方式后,通過消息發(fā)送給 payment-fnf-demo 函數(shù),然后將支付方式返回,于是流轉(zhuǎn)到 paymentCombination 節(jié)點通過判斷支付方式流轉(zhuǎn)到具體處理支付邏輯的節(jié)點和函數(shù)。

4. zhifubao 節(jié)點

看一個 zhifubao 節(jié)點:

choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price            
              - target: orderNum
                source: $input.orderNum
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken

節(jié)點的 resourceArn 和之前兩個節(jié)點的不同,這里配置的是函數(shù)計算中函數(shù)的 ARN,也就是說當(dāng)流程流轉(zhuǎn)到這個節(jié)點時會觸發(fā) zhifubao-fnf-demo 函數(shù),該函數(shù)是一個事件觸發(fā)函數(shù),但不需要創(chuàng)建任何觸發(fā)器。流程將訂單金額、訂單號、支付方式傳給 zhifubao-fnf-demo 函數(shù)。

zhifubao-fnf-demo 函數(shù):

# -*- coding: utf-8 -*-
import logging
import json
import requests
import urllib.parse
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest
from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest
def handler(event, context):
  region = "cn-xxx"
  account_id = "xxx"
  ak_id = "xxx"
  ak_secret = "xxx"
  fnf_client = AcsClient(
    ak_id,
    ak_secret,
    region
  )
  logger = logging.getLogger()
  logger.info(event)
  bodyJson = json.loads(event)
  price = bodyJson["price"]
  taskToken = bodyJson["taskToken"]
  orderNum = bodyJson["orderNum"]
  paymentMethod = bodyJson["paymentMethod"]
  logger.info("price:">

代碼邏輯很簡單,接收到金額后,將金額打 8 折,然后將價格更新回訂單。其他支付方式的節(jié)點和函數(shù)如法炮制,變更實現(xiàn)邏輯就可以。在這個示例中,微信支付打了 5 折,銀聯(lián)支付打 7 折。

以上是一個基于 Serverless 的 FC 實現(xiàn)的工作流,模擬構(gòu)建了一個訂單模塊,規(guī)則包括:

  • 配置商家和支付方式的元數(shù)據(jù)規(guī)則;

  • 確認(rèn)支付頁面的元數(shù)據(jù)規(guī)則。

在實際項目中,需要將可定制的部分抽象為元數(shù)據(jù)描述,需要有配置界面供運營或商家定制支付方式也就是元數(shù)據(jù)規(guī)則,然后前后端頁面基于元數(shù)據(jù)信息展示相應(yīng)的內(nèi)容。

如果之后需要接入新的支付方式,只需要在 paymentCombination 路由節(jié)點中確定好路由規(guī)則,之后增加對應(yīng)的支付方式函數(shù)即可,通過增加元數(shù)據(jù)配置項,就可以在頁面展示新加的支付方式,并路由到新的支付函數(shù)中。

經(jīng)過整篇文章相信很多人對于 Serverless 的定義,以及如何基于現(xiàn)有的公有云系統(tǒng)的 Serverless 功能實現(xiàn)商業(yè)能力已經(jīng)有了一定的了解,甚至基于此有實力的公司可以自研一套 Serverless 平臺。當(dāng)然思想是相同的,其實文中很多邏輯與理論不止適用于 Serverless,就是我們?nèi)粘;谖⒎?wù)的平臺化/中臺化解決方案,都可以從中獲取設(shè)計營養(yǎng)在工作中應(yīng)用。

上述就是小編為大家分享的Serverless怎么將配置化思想復(fù)用到平臺系統(tǒng)中了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI