您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)Serverless怎么將配置化思想復(fù)用到平臺系統(tǒng)中,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
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 時代。
相信你有過這樣的經(jīng)歷,在一個 Web 界面上,左側(cè)寫代碼,右側(cè)展示執(zhí)行效果。
寫的是代碼塊,代碼數(shù)量不會特別大;
代碼運行速度快;
支持多種編程語言;
可以支持不可預(yù)計的流量洪峰沖擊。
抽象來說,前端只需要將代碼片段和編程語言的標(biāo)識傳給 Server 端即可,等待響應(yīng)結(jié)果。Server 端可以針對于不同編程語言進(jìn)行 runtime 分類、預(yù)處理等工作。
很多人把 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 消耗的成本是非常低的。
首先新建一個服務(wù)名稱;
選定服務(wù)部署的地區(qū)(背后幫助你就近部署在目標(biāo)機房);
選擇是否打開調(diào)試日志(開發(fā)過程開啟,線上運行時可關(guān)閉)。
有了服務(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。
當(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 的精髓。
工作流可以用順序、分支、并行等方式來編排任務(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ù)流程的定制。
舉個例子,比如餐飲場景下不同商家可以配置不同的支付方式,可以走微信支付、銀聯(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) } }) }
先看下第一個 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):
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ā)送用戶選擇的支付方式和金額。
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ù)。
看一個 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è)資訊頻道。
免責(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)容。