Introduction: It takes a long time to build an APAAS platform. Of course, it can also achieve the flexibility and expansibility of the existing system based on the Serverless solution of some public cloud products, so as to achieve customization for different customers.

Writing in the front

Salesforce is the leader in the SaaS field, and its CRM concept has expanded to include Marketing, Sales, Service, and more. So how did Salesforce become the solution for these three industries? Benefit from Salesforce’s powerful APaS platform.

ISVs, internal implementors and customers can all build their own industries from their own dimensions based on the APaS platform to achieve business customization and even industry customization. Before that, only SaaS products were available in the direction of Sales, while Marketing and Service were the solutions of their own ISVs in their respective industries. So Salesforce has gone from being a SaaS company to being an APaS platform company.

It takes a long time to build an APAAS platform. Of course, it can also achieve the flexibility and expansibility of the existing system based on the Serverless solution of some public cloud products, so as to achieve customization for different customers.

What is a Serverless

ServerLess consists of two parts, Server and Less.

  • The former can be understood as its solution scope on the server side;
  • The latter can be translated as a small amount;

The combination is a server-side solution with less server-side intervention.

The opposite of Serverless is ServerFull, which might be easier to understand by comparing the concept.

In the ServerFull era, the R&D delivery process typically had three roles: RD, PM, and QA.

RD carries out functional development according to PM PRD, delivers to QA for testing, and releases to server after testing. Operation and maintenance personnel plan server specifications, quantity, machine room deployment, node expansion capacity, etc. This era is more handled by manpower is the era of ServerFull.

Then came the era of DevOps. In this era, the operation and maintenance of their own development of a set of operation and maintenance console, can let the R & D students on the console to carry out their own service observation, data query, operation and maintenance processing, operation and maintenance students work much easier, this stage mainly released the operation and maintenance of students manpower.

In Serverless era, the operations console ability more and more rich, can realize automatic enlarge shrinks according to the configuration, performance monitoring, and the conversation line, at the same time intrusion into research and development process, such as automatic release line code, compile, packaging, quality monitoring, such as gray scale distribution, elastic expansion shrinkage process basic don’t need to deal with the human, This is the era of Serverless.

How do you use the Serverless

I’m sure you’ve had the experience of writing code on the left side of a Web interface and showing the implementation effect on the right side.

  • You’re writing blocks of code, not too much code;
  • The code runs fast;
  • Support multiple programming languages;
  • Can support unpredictable flow peak impact.

AliCloud solution to see how to support multilingual architecture:

In the abstract, the front end simply passes the code snippet and programming language identity to the Server side and waits for the response. The Server side can carry out runtime classification and preprocessing for different programming languages.

How do Serverless

A lot of people think of Serverless as FC (Function Compute), where you don’t have to build your own IT infrastructure, you just need to code and upload the code. Function computation will prepare computing resources for you on demand, run flexibly and reliably, and provide governance capabilities such as trace, log query, monitoring alerts, etc.

Such as:

There are services and functions in FC. A service can contain more than one function. We can use microservices to understand that we have built a microservice architecture through Golang or Java, and the FC service is one of the classes, and the FC function is one of the methods in the class:

The difference is that Java-built microservices can only run Java class code, Golang classes can only run code written by Go, and FC functions can install runtimes in different languages and support running programs in different languages.

After the analogy is understood, let’s take a look at how to call FC functions. The general FC solution has the concept of a trigger. Examples include HTTP triggers, object storage triggers, logging service triggers, timing task triggers, CDN triggers, message queue triggers, and so on. A trigger is an abstract closure of a FC function call. For example, an HTTP trigger is usually analogous to an HTTP request event of the gateway, or an image is uploaded under the specified object storage path. The entry of these trigger events can be a trigger.

After the trigger produces an event, the FC function can be called. The function can perform logic such as downloading an image or registering a user.

This is the lifecycle of the FC from the trigger to the FC function logic processing.

So how does FC achieve high availability?

In fact, the underlying code of each function is running on a set of IaaS platform. Using IaaS resources, we can set the memory configuration needed for each function to run the code, such as the minimum 128M, the maximum 3G, etc. Developers do not need to care about what server the code is running on, do not need to care about how many function instances are started to support the current scenario, do not need to pay attention to the elastic scaling problem behind, these are all converged behind the FC.

There are two high availability strategies as shown in the figure:

  • Give a function the number of concurrent instances, say three, so that when three requests come in, the function starts only one instance, but three threads to run the logic.
  • When the thread reaches its limit, another instance of the function is pulled.

Similar to the thread pool scenario.

So how does Serverless help?

  • High efficiency: if you add a new language, you just need to create a corresponding Runtime FC function.
  • High availability: through multi-threading, multi-instance two ways to ensure high availability, and the function instance expansion capacity is completely self-handled by FC, do not need any configuration operation and maintenance;
  • Low cost: Function instances are not pulled and are not billed without trigger requests, so the cost of FC consumption is very low during traffic valleys or at night.

How do I create an FC on a cloud platform

  1. Create a service

= = = = = = = =

  • First create a new service name.
  • Select the area where the service is deployed (the back helps you deploy to the nearest target machine room);
  • Select whether to turn on the debug log (development is on, but offline can be turned off).
  1. Create a function

= = = = = = = =

Once you have the service, you can create functions, such as selecting functions based on HTTP requests.

  • Select the service bound by the function;
  • Set the function name;
  • Select the Runtime environment;
  • Whether the elasticity of the function instance is required;
  • Function entry (the target method called directly by the trigger);
  • Function execution memory;
  • Function execution timeout;
  • Sets instance concurrency.

Configure a trigger, such as an HTTP trigger, and then bind the function name to the trigger. Since it is an HTTP access, you can select the access authentication, authentication method, and request method POST or GET.

  1. The code

= = = = = = = =

When the function is created, you can enter the function and see the description, code execution history, trigger type, log query page, and so on. If it is an HTTP trigger, the HTTP trigger path needs to be configured.

As you can see, like a function in the class, the context request will be called here and executed directly.

Python code, for example:

# -*- 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): # fetch code request_body = environ['wsgi.input'].read(request_body_size) codeStr = Urllib.parse. Unquote (request_body.decode("GBK")) # Because the object in the body contains both code and input attributes, Split ('&') code = codeArr[0][5:] inputStr = codeArr[1][6:] # Save user code as py file / TMP/TMP FileName = '/ TMP /' + STR (int(time.time())) + '.py' f = open(fileName, '/ TMP /' + STR (int(time.time())) + '.py' f = open(fileName, F = open(fileName, "a") f.write(code) f.lose () # P =subprocess. Popen("python "+ fileName, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stdin=subprocess.PIPE, Stderr =subprocess.PIPE, shell=True, encoding='utf-8') # inputStr =subprocess.PIPE, shell=True, encoding='utf-8') = ' ': Write (inputStr + "n") p.stdin.flush() # Fetch the result of code execution on standard output r = p.stout.read () status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [r.encode('UTF-8')]

The process is as follows:

  • The front end passes in a snippet of code in a string format.
  • In the FC function to get the incoming code string, intercepted the code content and input content;
  • Save the code as a py file named after the timestamp and stored in the/TMP directory of FC functions. Each function has its own independent/TMP directory.
  • Import time library code;
  • Create subprocess through subprocess, execute py file saved in/TMP directory through py command in shell mode;
  • Finally, the result of the read execution is returned to the front end.

The front-end calls the FC function:

The whole process only needs the front end to pass the code into the FC function inside, the whole Server side each link does not need to research and development and operation and maintenance students care, reflects the essence of Serverless.

Coordinating workflows with Serverless

Workflow can arrange task execution in order, branch, parallel, etc. After that, the process can reliably coordinate task execution according to the set steps, track the state switch of each task, and execute the defined retry logic when necessary to ensure smooth execution of the process.

The workflow process monitors the execution of the workflow through logging and auditing, which facilitates the diagnosis and debugging of the process.

The core of system flexibility and expansibility is service choreography, so what we need to do is to sort out, split and remove the functions that the existing system internal users want to customize, and combine with the stateless ability provided by FC to arrange these function points, so as to realize the customization of business processes.

Businesses that need to flexibly configure their workflows

For example, in the catering scene, different merchants can configure different payment methods, such as WeChat payment, UnionPay payment and Alipay payment. Can support three at the same time, can also be a certain, can pay, can also be points exchange, etc. Without a well-configured process solution, there are a lot of hard-coded rule judgment conditions in the system, and the system iteration is an unsustainable process.

An FC-built workflow can solve this problem elegantly, such as the following:

The above process is a user-side process, and the next step is to transform it into an application-side process and create a workflow with the constrained FDL, as shown in the figure below:

The FDL code is as follows:

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

The example shows that Serverless FC can achieve flexible workflow.

How is the process triggered?

After the user selects the product and fills in the address, the process can be triggered automatically by pulling the product and the order context.

In the context of micro-service, many capabilities are not closed loop within single code logic, but are often connected to multiple business systems, such as series multiple OpenAPI interfaces to realize the whole process:

If you want to use the process engine, you need to carry out the relevant record authentication:

@Configuration public class FNFConfig { @Bean public IAcsClient createDefaultAcsClient(){ DefaultProfile profile = DefaultProfile. GetProfile (cn - "XXX", / / region ID "ak", / / RAM account the AccessKey ID "sk"); // Access Key Secret IACsclient client = new DefaultACsclient (profile); return client; }}

How does the process in startFNF string together:

  • Enter the name of the process to start, such as each order number as the start process instance name;
  • The process instance name after the process is started;
  • Start input parameters, such as business parameters, such as a JSON that contains contextual information about the product, merchant, address, order, etc.
@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);
    }

Let’s look at 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);
    }
  • The first part is to start the process;
  • The second part is to create order pairs and simulate warekeeping.

How is the front end called?

In the front end, after clicking the next step in the Select Goods and Merchantpage, the HTTP protocol interface/startFnf /{fnfname}/{execuname}/{input} is called via GET. Corresponding to the Java method above.

  • Fnfname: The name of the process to start;
  • ExecuName: Generates a UUID randomly as the order number and as the name of the startup process instance.
  • Input: Construct the product, merchant, order number, and address into a JSON string and pass it into the process.
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 node

= = = = = = = = = = = = = = = = = = =

Let’s look at the first FDL node definition:

- 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: Node name;
  • Timeoutseconds: The amount of time a node has waited before jumping to the OrderCanceled node indicated by the GOTO branch.
  • Pattern: Set to WaitForCallback, indicating that it needs to wait for confirmation;
  • InputMappings: This node is an inputMappings;

    • TaskToken: Token automatically generated by the Serverless workflow;
    • Products: selected goods;
    • Supplier: A selected merchant;
    • Address: Room service address;
    • OrderNum: order number;
  • OutputMappings: The output parameters of this node;

    • PaymentCombination: The payment method supported by the merchant;
    • OrderNum: order number;
  • Catch: Catch an exception and jump to another branch.

Serverless workflows support the integration of multiple cloud services, with other services as units of execution for task steps. The service integration mode is realized through FDL expression. In the task step, ResourceLearn can be used to define the target service for integration, and Pattern can be used to define the integration pattern.

/ Topics/GenerateInfo-fnf-demo-jiyuan /messages in Resourceearn When the GenerateInfo node fires, a message is sent to generateInfo-fnf-demo-jiyuantopic. The body and parameters of the message are specified in the ServiceParams object zhi’d. MessageBody is the MessageBody, and $is configured to mean that the MessageBody is generated with the input mapping InputMappings.

GenerateInfo FNF – demo function:

The message sent to generateInfo-fnf-demo-jiyuantopic contains item information, merchant information, address, and order number, indicating the beginning of an order placing process. If there is a sending message, there must be an receiving message for subsequent processing. In the function calculation console, create a service, and under the service create an event trigger function named GenerateInfo-fnf-demo. Select Python Runtime here:

Create an MNS trigger and select listen generateInfo-fnf-demo-jiyuantopic:

Open the Message Service MNS console and create generateInfo-fnf-demo-jiyuantopic:

Next, write the function code:

— 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. Build Serverless workflow 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. The information in the event receives the message content in the Topic generateinfo-fnf-demo-jiyuan and converts it into a JSON object

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. To judge which merchants use what combination of payment methods, the example here is relatively simple and crude, under normal circumstances, should use metadata configuration to obtain

paymentcombination = “”

if supplier == “haidilao”:

paymentcombination = "zhifubao,weixin"

else:

paymentcombination = "zhifubao,weixin,unionpay"

4. Call the interface exposed by the Java service to update the order information, mainly to update the payment method

url = “http://xx.xx.xx.xx:8080/setPaymentCombination/” + orderNum + “/” + paymentcombination + “/0”

x = requests.get(url)

5. Give the GenerateInfo node a response and return data, in this case the order number and payment method

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’

The code is divided into five parts:

  • Build the Serverless workflow Client;
  • The information inside the event receives the message content in topicGenerateInfo-fnf-demo-jiyuan and converts it into a JSON object;
  • The example here is simple and crude to determine what combination of payment methods is used by which merchants. Under normal circumstances, metadata configuration should be used to obtain it. For example, there is a merchant information configuration function in the system. By configuring the payment methods supported by the merchant on the interface, the metadata configuration information is formed, and the query interface is provided, where the query is conducted.
  • Call the interface exposed by the Java service, update the order information, mainly update the payment method;
  • Give the GenerateInfo node a response and return the data, in this case the order number and the method of payment. Because the pattern for this node is WaitForCallback, you need to wait for the result of the response.

The generateInfo-fnf-demo function is configured with an MNS trigger that triggers the execution of the generateInfo-fnf-demo function when topicGenerateInfo-fnf-demo-jiyuan message is received.

2. Payment node

Next is the FDL code definition for Payment:

* 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 When the process flows to the Payment node, the user is notified to the payment page. ! [](https://mp.toutiao.com/mp/agw/article_material/open_image/get?code=MDIxZWMxNzBiMTZmOTRiY2FhYzhlMzQzZDQxYmU2MWMsMTYxMT U0MDK5MZQ1OA ==) The Payment node will send a message to the topicPayment-fnf-demo-jiyuan of MNS, which will trigger the Payment-fnf-demo function. Payment-fnf-demo: The Payment-fnf-demo function is created in a similar way to the generateInfo-Fnf-demo function.

– 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:======================" + 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'
The core idea of the above code is to wait for the user to select a payment method on the payment page to confirm the payment. Queues using MNS are used to simulate waiting. The cycle waits for a message in the receiving queue Payment-queue-FNF-DEMO, and returns the order number, the specific payment method selected by the user and the amount to the Payment node after receiving the message. Front-end payment method selection page: after passing the GenerateInfo node, the payment method information of this order has already been provided. Therefore, for the user, after filling in the product, merchant and address, the page to which he jumps is the confirmation payment page, which contains the payment method supported by the merchant. After entering this page, it will request the interface exposed by the Java service, obtain the order information, and display different payment methods on the page according to the payment method. ! [](https://mp.toutiao.com/mp/agw/article_material/open_image/get?code=ZDlkYTM4NmQ2NGQ3OTU2OGE1NzE3Mzk0NGIwZDIwYWMsMTYxMT U0MDk5MzQ1OA==) The code snippet is as follows:! [](https://mp.toutiao.com/mp/agw/article_material/open_image/get?code=ZDJlZDlmMzQwMjAxNjg3MDU5MmI0ODJlNjkxZTExNGYsMTYxMT U0MDK5MZQ1OA ==) When the user selects a payment method and clicks the Submit Order button, a message is sent to the Payment-Queue - FNF-DEMO queue, which tells the Payment-FNF-DEMO function to continue the subsequent logic. An HTTP trigger type function is used to implement the logic of sending messages to MNS. The PaymentMethod-FNF-demo function code is:

– 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" " 
                     "}" % (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]
The logic of the function is simple. It sends the payment method and amount selected by the user to the queue Payment-Queue - FNF-DEMO of MNS. ! [](https://mp.toutiao.com/mp/agw/article_material/open_image/get?code=ZGUxYzE1Yjk3MDVmZTYzYzJhNDdjZGQ5YWYxMGQzZTUsMTYxMT U0MDK5MZQ1OA ==) 3. PaymentCombination node ========================= PaymentCombination node is a routing node that routes to different nodes by determining a parameter. The PaymentMethod is used as the judgment condition:
  • 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
The process is that after the user chooses the payment method, he sends a message to the Payment-FNF-Demo function, and then returns the payment method. Then the payment method is transferred to the node of PaymentCombination, which determines the payment method and sends it to the node and function that specifically process the payment logic. 4. Zhifubao node =============== Look at a Zhifubao node:

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
The ResourceLearn of node is different from that of the previous two nodes. What is configured here is the ARN of the function in the function calculation, that is to say, when the flow flows to this node, the zhifubao-fnf-demo function will be triggered. This function is an event triggering function, but does not need to create any triggers. The process passes the order amount, order number and payment method to the zhifubao-fnf-demo function. Zhifubao FNF - demo function:

– 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”}

The code logic is simple, after receiving the amount, discount the amount by 20%, and then update the price back to the order. The nodes and functions of other payment methods can be replicated by changing the implementation logic. In this example, WeChat is 50% off and UnionPay is 30% off. The complete process ==== The OrderCompleted and OrderCanceled nodes in the process do not do any logic. The process is as follows:! [](https://mp.toutiao.com/mp/agw/article_material/open_image/get?code=MjdjZTVlY2Q0M2JmYzQ2OTIxNGFjMWE2YmY4N2JhODMsMTYxMT U0MDk5MzQ1OQ==) The node flow seen from the Serverless workflow looks like this:! [](https://mp.toutiao.com/mp/agw/article_material/open_image/get?code=ZDRlMGVkMTBhNTIwNDBmNGQzYjNlZmFiMjgzMGU4NjMsMTYxMT U0MDK5MZQ1OQ ==) is written in the following ==== above is a workflow based on Serverless FC implementation, simulation built an order module, rules include: * Configure merchant and payment method metadata rules; * Confirm the metadata rules for the payment page. In a real project, the customizable part needs to be abstracted into metadata description, and there needs to be a configuration interface for operators or merchants to customize the payment method, that is, metadata rules, and then the front and back end pages show the corresponding content based on the metadata information. If you need to access a new payment method later, you only need to determine the routing rules in the routing node of PaymentCombination, and then add the corresponding payment method function. By adding the metadata configuration item, the newly added payment method can be displayed on the page and routed to the new payment function. After the whole article I believe that a lot of people for the definition of Serverless, and how to based on the existing public cloud system Serverless function to achieve business capabilities have a certain understanding, and even based on the strength of the company can develop a set of Serverless platform. Of course, the idea is the same. In fact, a lot of logic and theory in the paper are not only applicable to Serverless, but also to our daily platform/mid-platform solutions based on micro-services. We can get design nutrition from it and apply it in our work. Author: brother chun archenemy url [sic] (https://developer.aliyun.com/article/781356?utm_content=g_1000233201)