


version: v1beta1type: flowtimeoutSeconds: 3600steps:- type: taskname: generateInfotimeoutSeconds: 300resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messagespattern: waitForCallbackinputMappings:- target: taskTokensource: $context.task.token- target: productssource: $input.products- target: suppliersource: $input.supplier- target: addresssource: $input.address- target: orderNumsource: $input.orderNum- target: typesource: $context.step.nameoutputMappings:- target: paymentcombinationsource: $local.paymentcombination- target: orderNumsource: $local.orderNumserviceParams:MessageBody: $Priority: 1catch:- errors:- FnF.TaskTimeoutgoto: orderCanceled-type: taskname: paymenttimeoutSeconds: 300resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messagespattern: waitForCallbackinputMappings:- target: taskTokensource: $context.task.token- target: orderNumsource: $local.orderNum- target: paymentcombinationsource: $local.paymentcombination- target: typesource: $context.step.nameoutputMappings:- target: paymentMethodsource: $local.paymentMethod- target: orderNumsource: $local.orderNum- target: pricesource: $local.price- target: taskTokensource: $input.taskTokenserviceParams:MessageBody: $Priority: 1catch:- errors:- FnF.TaskTimeoutgoto: orderCanceled- type: choicename: paymentCombinationinputMappings:- target: orderNumsource: $local.orderNum- target: paymentMethodsource: $local.paymentMethod- target: pricesource: $local.price- target: taskTokensource: $local.taskTokenchoices:- condition: $.paymentMethod == "zhifubao"steps:- type: taskname: zhifubaoresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demoinputMappings:- target: pricesource: $input.price- target: orderNumsource: $input.orderNum- target: paymentMethodsource: $input.paymentMethod- target: taskTokensource: $input.taskToken- condition: $.paymentMethod == "weixin"steps:- type: taskname: weixinresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demoinputMappings:- target: pricesource: $input.price- target: orderNumsource: $input.orderNum- target: paymentMethodsource: $input.paymentMethod- target: taskTokensource: $input.taskToken- condition: $.paymentMethod == "unionpay"steps:- type: taskname: unionpayresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demoinputMappings:- target: pricesource: $input.price- target: orderNumsource: $input.orderNum- target: paymentMethodsource: $input.paymentMethod- target: taskTokensource: $input.taskTokendefault:goto: orderCanceled- type: taskname: orderCompletedresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/orderCompletedend: true- type: taskname: orderCanceledresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/cancerOrder


Java 启动流程
<dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-core</artifactId><version>[4.3.2,5.0.0)</version></dependency><dependency><groupId>com.aliyun</groupId><artifactId>aliyun-java-sdk-fnf</artifactId><version>[1.0.0,5.0.0)</version></dependency>
public class FNFConfig {public IAcsClient createDefaultAcsClient(){DefaultProfile profile = DefaultProfile.getProfile("cn-xxx", // 地域ID"ak", // RAM 账号的AccessKey ID"sk"); // RAM 账号Access Key SecretIAcsClient client = new DefaultAcsClient(profile);return client;}}
public StartExecutionResponse startFNF( String fnfName,String execuName,String inputStr) throws ClientException {JSONObject jsonObject = new JSONObject();jsonObject.put("fnfname", fnfName);jsonObject.put("execuname", execuName);jsonObject.put("input", inputStr);return fnfService.startFNF(jsonObject);}
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);}
VUE 选择商品/商家页面
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)}})}
- type: taskname: generateInfotimeoutSeconds: 300resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messagespattern: waitForCallbackinputMappings:- target: taskTokensource: $context.task.token- target: productssource: $input.products- target: suppliersource: $input.supplier- target: addresssource: $input.address- target: orderNumsource: $input.orderNum- target: typesource: $context.step.nameoutputMappings:- target: paymentcombinationsource: $local.paymentcombination- target: orderNumsource: $local.orderNumserviceParams:MessageBody: $Priority: 1catch:- errors:- FnF.TaskTimeoutgoto: orderCanceled
taskToken:Serverless 工作流自动生成的 Token。
products:选择的商品。
supplier:选择的商家。
address:送餐地址。
orderNum:订单号。
paymentcombination:该商家支持的支付方式。
orderNum:订单号。
# -*- coding: utf-8 -*-import loggingimport jsonimport timeimport requestsfrom aliyunsdkcore.client import AcsClientfrom aliyunsdkcore.acs_exception.exceptions import ServerExceptionfrom aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequestfrom aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequestdef handler(event, context):# 1. 构建Serverless工作流Clientregion = "cn-hangzhou"account_id = "XXXX"ak_id = "XXX"ak_secret = "XXX"fnf_client = AcsClient(ak_id,ak_secret,region)logger = logging.getLogger()# 2. event内的信息即接受到Topic generateInfo-fnf-demo-jiyuan中的消息内容,将其转换为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. 判断什么商家使用什么样的支付方式组合,这里的示例比较简单粗暴,正常情况下,应该使用元数据配置的方式获取paymentcombination = ""if supplier == "haidilao":paymentcombination = "zhifubao,weixin"else:paymentcombination = "zhifubao,weixin,unionpay"# 4. 调用Java服务暴露的接口,更新订单信息,主要是更新支付方式url = "http://xx.xx.xx.xx:8080/setPaymentCombination/" + orderNum + "/" + paymentcombination + "/0"x = requests.get(url)# 5. 给予generateInfo节点响应,并返回数据,这里返回了订单号和支付方式output = "{\"orderNum\": \"%s\", \"paymentcombination\":\"%s\" " \"}" % (orderNum, paymentcombination)request = ReportTaskSucceededRequest.ReportTaskSucceededRequest()request.set_Output(output)request.set_TaskToken(taskToken)resp = fnf_client.do_action_with_exception(request)return 'hello world'
- type: taskname: paymenttimeoutSeconds: 300resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messagespattern: waitForCallbackinputMappings:- target: taskTokensource: $context.task.token- target: orderNumsource: $local.orderNum- target: paymentcombinationsource: $local.paymentcombination- target: typesource: $context.step.nameoutputMappings:- target: paymentMethodsource: $local.paymentMethod- target: orderNumsource: $local.orderNum- target: pricesource: $local.price- target: taskTokensource: $input.taskTokenserviceParams:MessageBody: $Priority: 1catch:- errors:- FnF.TaskTimeoutgoto: orderCanceled
# -*- coding: utf-8 -*-import loggingimport jsonimport osimport timeimport loggingfrom aliyunsdkcore.client import AcsClientfrom aliyunsdkcore.acs_exception.exceptions import ServerExceptionfrom aliyunsdkcore.client import AcsClientfrom aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequestfrom aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequestfrom mns.account import Account # pip install aliyun-mnsfrom 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 = Truewhile isLoop:try:recv_msg = my_queue.receive_message(30)isLoop = False# body = json.loads(recv_msg.message_body)logger.info("recv_msg.message_body:======================" + recv_msg.message_body)msgJson = json.loads(recv_msg.message_body)my_queue.delete_message(recv_msg.receipt_handle)# orderCode = int(time.time())task_token = eventJson["taskToken"]orderNum = eventJson["orderNum"]output = "{\"orderNum\": \"%s\", \"paymentMethod\": \"%s\", \"price\": \"%s\" " \"}" % (orderNum, msgJson["paymentMethod"], msgJson["price"])request = ReportTaskSucceededRequest.ReportTaskSucceededRequest()request.set_Output(output)request.set_TaskToken(task_token)resp = fnf_client.do_action_with_exception(request)except Exception as e:logger.info("new loop")return 'hello world'
# -*- coding: utf-8 -*-import loggingimport urllib.parseimport jsonfrom mns.account import Account # pip install aliyun-mnsfrom 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 headerspasstry:request_body_size = int(environ.get('CONTENT_LENGTH', 0))except (ValueError):request_body_size = 0request_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\" " \"}" % (paymentMethodJson["paymentMethod"], paymentMethodJson["price"])msg = Message(output)my_queue.send_message(msg)status = '200 OK'response_headers = [('Content-type', 'text/plain')]start_response(status, response_headers)return [HELLO_WORLD]
- type: choicename: paymentCombinationinputMappings:- target: orderNumsource: $local.orderNum- target: paymentMethodsource: $local.paymentMethod- target: pricesource: $local.price- target: taskTokensource: $local.taskTokenchoices:- condition: $.paymentMethod == "zhifubao"steps:- type: taskname: zhifubaoresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demoinputMappings:- target: pricesource: $input.price- target: orderNumsource: $input.orderNum- target: paymentMethodsource: $input.paymentMethod- target: taskTokensource: $input.taskToken- condition: $.paymentMethod == "weixin"steps:- type: taskname: weixinresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demoinputMappings:- target: pricesource: $input.price- target: orderNumsource: $input.orderNum- target: paymentMethodsource: $input.paymentMethod- target: taskTokensource: $input.taskToken- condition: $.paymentMethod == "unionpay"steps:- type: taskname: unionpayresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demoinputMappings:- target: pricesource: $input.price- target: orderNumsource: $input.orderNum- target: paymentMethodsource: $input.paymentMethod- target: taskTokensource: $input.taskTokendefault:goto: orderCanceled
choices:- condition: $.paymentMethod == "zhifubao"steps:- type: taskname: zhifubaoresourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demoinputMappings:- target: pricesource: $input.price- target: orderNumsource: $input.orderNum- target: paymentMethodsource: $input.paymentMethod- target: taskTokensource: $input.taskToken
# -*- coding: utf-8 -*-import loggingimport jsonimport requestsimport urllib.parsefrom aliyunsdkcore.client import AcsClientfrom aliyunsdkcore.acs_exception.exceptions import ServerExceptionfrom aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequestfrom aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequestdef 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.8logger.info("newPrice:" + str(newPrice))url = "http://xx.xx.xx.xx:8080/setPaymentCombination/" + orderNum + "/" + paymentMethod + "/" + str(newPrice)x = requests.get(url)return {"Status":"ok"}
流程中的 orderCompleted 和 orderCanceled 节点没做什么逻辑,大家可以自行发挥,思路和之前的节点一样。所以完整的流程是这样:

以上内容作为抛砖引玉之石,探索 Serverless 的应用场景,来解决 SaaS 厂商灵活性和扩展性的痛点。大家如果有任何疑问也可以加入钉钉群:35712134 来寻找答案,我们不见不散!


