Serverless在SaaS领域的最佳实践
簡介:?特別對于當下的經(jīng)濟環(huán)境,SaaS廠商要明白,不能再通過燒錢的方式,只關(guān)注在自己的用戶數(shù)量上,而更多的要思考如何幫助客戶降低成本、增加效率,所以需要將更多的精力放在自己產(chǎn)品的定制化能力上。
作者:阿里云解決方案架構(gòu)師 計緣
隨著互聯(lián)網(wǎng)人口紅利逐漸減弱,基于流量的增長已經(jīng)放緩,互聯(lián)網(wǎng)行業(yè)迫切需要找到一片足以承載自身持續(xù)增長的新藍海,產(chǎn)業(yè)互聯(lián)網(wǎng)正是這一宏大背景下的新趨勢。我們看到互聯(lián)網(wǎng)浪潮正在席卷傳統(tǒng)行業(yè),云計算、大數(shù)據(jù)、人工智能開始大規(guī)模融入到金融、制造、物流、零售、文娛、教育、醫(yī)療等行業(yè)的生產(chǎn)環(huán)節(jié)中,這種融合稱為產(chǎn)業(yè)互聯(lián)網(wǎng)。而在產(chǎn)業(yè)互聯(lián)網(wǎng)中,有一塊不可小覷的領(lǐng)域是SaaS領(lǐng)域,它是ToB賽道的中堅力量,比如CRM、HRM、費控系統(tǒng)、財務(wù)系統(tǒng)、協(xié)同辦公等等。
SaaS系統(tǒng)面臨的挑戰(zhàn)
在消費互聯(lián)網(wǎng)時代,大家是搜索想要的東西,各個廠商在云計算、大數(shù)據(jù)、人工智能等技術(shù)基座之上建立流量最大化的服務(wù)與生態(tài),基于海量內(nèi)容分發(fā)與流量共享為邏輯構(gòu)建系統(tǒng)。而到了產(chǎn)業(yè)互聯(lián)網(wǎng)時代,供給關(guān)系發(fā)生了變化,大家是定制想要的東西,需要從供給與需求兩側(cè)出發(fā)進行雙向建設(shè),這個時候系統(tǒng)的靈活性和擴展性面臨著前所未有的挑戰(zhàn),尤其是ToB的SaaS領(lǐng)域。
特別對于當下的經(jīng)濟環(huán)境,SaaS廠商要明白,不能再通過燒錢的方式,只關(guān)注在自己的用戶數(shù)量上,而更多的要思考如何幫助客戶降低成本、增加效率,所以需要將更多的精力放在自己產(chǎn)品的定制化能力上。
如何應(yīng)對挑戰(zhàn)
SaaS領(lǐng)域中的佼佼者Salesforce,將CRM的概念擴展到Marketing、Sales、Service,而這三塊領(lǐng)域中只有Sales有專門的SaaS產(chǎn)品,其他兩個領(lǐng)域都是各個ISV在不同行業(yè)的行業(yè)解決方案,靠的是什么?毋庸置疑,是Salesforce強大的aPaaS平臺。ISV、內(nèi)部實施、客戶均可以在各自維度通過aPaaS平臺構(gòu)建自己行業(yè)、自己領(lǐng)域的SaaS系統(tǒng),建立完整的生態(tài)。所以在我看來,現(xiàn)在的Salesforce已經(jīng)由一家SaaS公司升華為一家aPaaS平臺公司了。這種演進的過程也印證了消費互聯(lián)網(wǎng)和產(chǎn)業(yè)互聯(lián)網(wǎng)的轉(zhuǎn)換邏輯以及后者的核心訴求。
然而不是所有SaaS公司都有財力和時間去孵化和打磨自己的aPaaS平臺,但市場的變化、用戶的訴求是實實在在存在的。若要生存,就要求變。這個變的核心就是能夠讓自己目前的SaaS系統(tǒng)變得靈活起來,相對建設(shè)困難的aPaaS平臺,我們其實可以選擇輕量且有效的Serverless方案來提升現(xiàn)有系統(tǒng)的靈活性和可擴展性,從而實現(xiàn)用戶不同的定制需求。
Serverless工作流
在上一篇文章《資源成本雙優(yōu)化!看Serverless顛覆編程教育的創(chuàng)新實踐》中,已經(jīng)對Serverless的概念做過闡述了,并且也介紹了Serverless函數(shù)計算(FC)的概念和實踐。這篇文章中介紹一下構(gòu)建系統(tǒng)靈活性的核心要素服務(wù)編排——Serverless工作流。
Serverless 工作流是一個用來協(xié)調(diào)多個分布式任務(wù)執(zhí)行的全托管云服務(wù)。在 Serverless工作流中,可以用順序、分支、并行等方式來編排分布式任務(wù),Serverless工作流會按照設(shè)定好的步驟可靠地協(xié)調(diào)任務(wù)執(zhí)行,跟蹤每個任務(wù)的狀態(tài)轉(zhuǎn)換,并在必要時執(zhí)行您定義的重試邏輯,以確保工作流順利完成。Serverless工作流通過提供日志記錄和審計來監(jiān)視工作流的執(zhí)行,可以輕松地診斷和調(diào)試應(yīng)用。
下面這張圖描述了Serverless工作流如何協(xié)調(diào)分布式任務(wù),這些任務(wù)可以是函數(shù)、已集成云服務(wù)API、運行在虛擬機或容器上的程序。
看完Serverless工作流的介紹,大家可能已經(jīng)多少有點思路了吧。系統(tǒng)靈活性和可擴展性的核心是服務(wù)可編排,無論是以前的BPM還是現(xiàn)在的aPaaS。所以基于Serverless工作流重構(gòu)SaaS系統(tǒng)靈活性方案的核心思路,是將系統(tǒng)內(nèi)用戶最希望定制的功能進行梳理、拆分、抽離,再配合函數(shù)計算(FC)提供無狀態(tài)的能力,通過Serverless工作流進行這些功能點的編排,從而實現(xiàn)不同的業(yè)務(wù)流程。
通過函數(shù)計算FC和Serverless工作流搭建靈活的訂餐模塊
訂餐場景相信大家都不會陌生,在家叫外賣或者在餐館點餐,都涉及到這個場景。當下也有很多提供點餐系統(tǒng)的SaaS服務(wù)廠商,有很多不錯的SaaS點餐系統(tǒng)。隨著消費互聯(lián)網(wǎng)向產(chǎn)業(yè)互聯(lián)網(wǎng)轉(zhuǎn)換,這些SaaS點餐系統(tǒng)面臨的定制化的需求也越來越多,其中有一個需求是不同的商家在支付時會顯示不同的支付方式,比如從A商家點餐后付款時顯示支付寶、微信支付、銀聯(lián)支付,從B商家點餐后付款時顯示支付寶、京東支付。突然美團又冒出來了美團支付,此時B商家接了美團支付,那么從B商家點餐后付款時顯示支付寶、京東支付、美團支付。諸如此類的定制化需求越來越多,這些SaaS產(chǎn)品如果沒有PaaS平臺,那么就會疲于不斷的通過硬代碼增加條件判斷來實現(xiàn)不同商家的需求,這顯然不是一個可持續(xù)發(fā)展的模式。
那么我們來看看通過函數(shù)計算FC和Serverless工作流如何優(yōu)雅的解決這個問題。先來看看這個點餐流程:
通過Serverless工作流創(chuàng)建流程
首選我需要將上面用戶側(cè)的流程轉(zhuǎn)變?yōu)槌绦騻?cè)的流程,此時就需要使用Serverless工作流來擔(dān)任此任務(wù)了。
打開Serverless控制臺,創(chuàng)建訂餐流程,這里Serverless工作流使用流程定義語言FDL創(chuàng)建工作流,如何使用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
- FnF.TaskTimeout
- target: taskToken
- 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
- FnF.TaskTimeout
- target: taskToken
- 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
- target: price
- type: task
- 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
- target: price
- type: task
- 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
- target: price
- type: task
- target: orderNum
- 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
在解析整個流程之前,我先要說明的一點是,我們不是完全通過Serverless函數(shù)計算和Serverless工作流來搭建訂餐模塊,只是用它來解決靈活性的問題,所以這個示例的主體應(yīng)用是Java編寫的,然后結(jié)合了Serverless函數(shù)計算和Serverless工作流。下面我們來詳細解析這個流程。
啟動流程
按常理,開始點餐時流程就應(yīng)該啟動了,所以在這個示例中,我的設(shè)計是當我們選擇完商品和商家、填完地址后啟動流程:
這里我們通過Serverless工作流提供的OpenAPI來啟動流程。
Java啟動流程
這個示例我使用Serverless工作流的Java SDK,首先在POM文件中添加依賴:
@Configuration
public class FNFConfig {
}
再來看Controller中的startFNF方法,該方法暴露GET方式的接口,傳入三個參數(shù):
fnfname:要啟動的流程名稱。
execuname:流程啟動后的流程實例名稱。
input:啟動輸入?yún)?shù),比如業(yè)務(wù)參數(shù)。
@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);
}
再來看Service中的startFNF方法,該方法分兩部分,第一個部分是啟動流程,第二部分是創(chuàng)建訂單對象,并模擬入庫(示例中是放在Map里了):
啟動流程時,流程名稱和啟動流程實例的名稱是需要傳入的參數(shù),這里我將每次的訂單編號作為啟動流程的實例名稱。至于Input,可以根據(jù)需求構(gòu)造JSON字符串傳入。這里我將商品、商家、地址、訂單號構(gòu)造了JSON字符串在流程啟動時傳入流程中。
另外,創(chuàng)建了此次訂單的Order實例,并存在Map中,模擬入庫,后續(xù)環(huán)節(jié)還會查詢該訂單實例更新訂單屬性。
VUE選擇商品/商家頁面
前端我使用VUE搭建,當點擊選擇商品和商家頁面中的下一步后,通過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.axios.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)
}
})
}
generateInfo節(jié)點
第一個節(jié)點generateInfo,先來看看FDL的含義:
- 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,表示需要等待確認。inputMappings:該節(jié)點入?yún)ⅰ?br /> taskToken:Serverless工作流自動生成的Token。
products:選擇的商品。
supplier:選擇的商家。
address:送餐地址。
orderNum:訂單號。
outputMappings:該節(jié)點的出參。
paymentcombination:該商家支持的支付方式。
orderNum:訂單號。
catch:捕獲異常,跳轉(zhuǎn)到其他分支。
這里resourceArn和serviceParams需要拿出來單獨解釋。Serverless工作流支持與多個云服務(wù)集成,即將其他服務(wù)作為任務(wù)步驟的執(zhí)行單元。服務(wù)集成方式由FDL語言表達,在任務(wù)步驟中,可以使用resourceArn來定義集成的目標服務(wù),使用pattern定義集成模式。所以可以看到在resourceArn中配置acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages信息,即在generateInfo節(jié)點中集成了MNS消息隊列服務(wù),當generateInfo節(jié)點觸發(fā)后會向generateInfo-fnf-demo-jiyuanTopic中發(fā)送一條消息。那么消息正文和參數(shù)則在serviceParams對象中指定。MessageBody是消息正文,配置$表示通過輸入映射inputMappings產(chǎn)生消息正文。
- FnF.TaskTimeout
- target: taskToken
看完第一個節(jié)點的示例,大家可以看到,在Serverless工作流中,節(jié)點之間的信息傳遞可以通過集成MNS發(fā)送消息來傳遞,也是使用比較廣泛的方式之一。
generateInfo-fnf-demo函數(shù)
向generateInfo-fnf-demo-jiyuanTopic中發(fā)送的這條消息包含了商品信息、商家信息、地址、訂單號,表示一個下訂單流程的開始,既然有發(fā)消息,那么必然有接受消息進行后續(xù)處理。所以打開函數(shù)計算控制臺,創(chuàng)建服務(wù),在服務(wù)下創(chuàng)建名為generateInfo-fnf-demo的事件觸發(fā)器函數(shù),這里選擇Python Runtime:
創(chuàng)建MNS觸發(fā)器,選擇監(jiān)聽generateInfo-fnf-demo-jiyuanTopic。
打開消息服務(wù)MNS控制臺,創(chuàng)建generateInfo-fnf-demo-jiyuanTopic:
做好函數(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:” + bodyJson[“products”])
logger.info(“supplier:” + bodyJson[“supplier”])
logger.info(“address:” + bodyJson[“address”])
logger.info(“taskToken:” + bodyJson[“taskToken”])
supplier = bodyJson[“supplier”]
taskToken = bodyJson[“taskToken”]
orderNum = bodyJson[“orderNum”]
# 3. 判斷什么商家使用什么樣的支付方式組合,這里的示例比較簡單粗暴,正常情況下,應(yīng)該使用元數(shù)據(jù)配置的方式獲取
paymentcombination = “”
if supplier == “haidilao”:
paymentcombination = “zhifubao,weixin”
else:
paymentcombination = “zhifubao,weixin,unionpay”
因為generateInfo-fnf-demo函數(shù)配置了MNS觸發(fā)器,所以當TopicgenerateInfo-fnf-demo-jiyuan有消息后就會觸發(fā)執(zhí)行g(shù)enerateInfo-fnf-demo函數(shù)。
整個代碼分五部分:
構(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ù)配置信息,提供查詢接口,在這里進行查詢。
調(diào)用Java服務(wù)暴露的接口,更新訂單信息,主要是更新支付方式。
給予generateInfo節(jié)點響應(yīng),并返回數(shù)據(jù),這里返回了訂單號和支付方式。因為該節(jié)點的pattern是waitForCallback,所以需要等待響應(yīng)結(jié)果。
payment節(jié)點
我們再來看第二個節(jié)點payment,先來看FDL代碼:
- 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
當流程流轉(zhuǎn)到payment節(jié)點后,意味著用戶進入了支付頁面。
這時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)
該函數(shù)的核心思路是等待用戶在支付頁面選擇某個支付方式確認支付。所以這里使用了MNS的隊列來模擬等待。循環(huán)等待接收隊列payment-queue-fnf-demo中的消息,當收到消息后將訂單號和用戶選擇的具體支付方式以及金額返回給payment節(jié)點。
VUE選擇支付方式頁面
因為經(jīng)過generateInfo節(jié)點后,該訂單的支付方式信息已經(jīng)有了,所以對于用戶而言,當填完商品、商家、地址后,跳轉(zhuǎn)到的頁面就是該確認支付頁面,并且包含了該商家支持的支付方式。
當進入該頁面后,會請求Java服務(wù)暴露的接口,獲取訂單信息,根據(jù)支付方式在頁面上顯示不同的支付方式。代碼片段如下:
當用戶選定某個支付方式點擊提交訂單按鈕后,向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)
該函數(shù)的邏輯很簡單,就是向MNS的隊列payment-queue-fnf-demo發(fā)送用戶選擇的支付方式和金額。
VUE代碼片段如下:
paymentCombination節(jié)點
paymentCombination節(jié)點是一個路由節(jié)點,通過判斷某個參數(shù)路由到不同的節(jié)點,這里自然使用paymentMethod作為判斷條件。FDL代碼如下:
- 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é)點
我們具體來看一個zhifubao節(jié)點:
這個節(jié)點的resourceArn和之前兩個節(jié)點的不同,這里配置的是函數(shù)計算中函數(shù)的ARN,也就是說當流程流轉(zhuǎn)到這個節(jié)點時會觸發(fā)zhifubao-fnf-demo函數(shù),該函數(shù)是一個事件觸發(fā)函數(shù),但不需要創(chuàng)建任何觸發(fā)器。流程將訂單金額、訂單號、支付方式傳給zhifubao-fnf-demo函數(shù)。
zhifubao-fnf-demo函數(shù)
現(xiàn)在我們來看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:” + price)
newPrice = int(price) * 0.8
logger.info(“newPrice:” + str(newPrice))
url = “http://xx.xx.xx.xx:8080/setPaymentCombination/” + orderNum + “/” + paymentMethod + “/” + str(newPrice)
x = requests.get(url)
return {“Status”:“ok”}
示例中的代碼邏輯很簡單,接收到金額后,將金額打8折,然后將價格更新回訂單。其他支付方式的節(jié)點和函數(shù)如法炮制,變更實現(xiàn)邏輯就可以。在這個示例中,微信支付打了5折,銀聯(lián)支付打7折。
完整流程
流程中的orderCompleted和orderCanceled節(jié)點沒做什么邏輯,大家可以自行發(fā)揮,思路和之前的節(jié)點一樣。所以完整的流程是這樣:
從Serverless工作流中看到的節(jié)點流轉(zhuǎn)是這樣的:
總結(jié)
到此,我們基于Serverless工作流和Serverless函數(shù)計算構(gòu)建的訂單模塊示例就算完成了,在示例中,有兩個點需要大家注意:
配置商家和支付方式的元數(shù)據(jù)規(guī)則。
確認支付頁面的元數(shù)據(jù)規(guī)則。
因為在實際生產(chǎn)中,我們需要將可定制的部分都抽象為元數(shù)據(jù)描述,需要有配置界面制定商家的支付方式即更新元數(shù)據(jù)規(guī)則,然后前端頁面基于元數(shù)據(jù)信息展示相應(yīng)的內(nèi)容。
所以如果之后需要接入其他的支付方式,只需在paymentCombination路由節(jié)點中確定好路由規(guī)則,然后增加對應(yīng)的支付方式函數(shù)即可。通過增加元數(shù)據(jù)配置項,就可以在頁面顯示新加的支付方式,并且路由到處理新支付方式的函數(shù)中。
以上內(nèi)容作為拋磚引玉之石,探索Serverless的應(yīng)用場景,來解決SaaS廠商靈活性和擴展性的痛點。大家如果有任何疑問也可以加入釘釘群35712134來尋找答案,我們不見不散!
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的Serverless在SaaS领域的最佳实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云机器学习怎么玩?这本新手入门指南揭
- 下一篇: 阿里云AHAS Chaos:应用及业务高