The author | brother chun archenemy source | Serverless public number

Writing in the front

Salesforce is a leader in the SaaS field, and its CRM concept has been extended to Marketing, Sales, Service and other fields. So how did Salesforce become the solution for all three industries? Thanks to Salesforce’s strong aPaaS platform.

Isvs, internal implementations, and customers can build their own industries based on aPaaS platforms from their own dimensions to achieve business customization and even industry customization. Before that, there were only dedicated SaaS products in the Sales direction, while Marketing and Service were solutions by their own ISVs in their respective industries. So Salesforce has gone from being a SaaS company to an aPaaS platform company.

It takes a long time to build an aPaaS platform. Of course, the flexibility and expansibility of the existing system can also be realized 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 having a solution scope on the server side;
  • The latter can be translated as a small amount;

Together, this is a server-side solution with less server-side intervention.

The opposite of Serverless is Serverfull, which might be easier to understand.

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

RD develops functions according to PM PRD, delivers to QA for testing, and releases to server after testing. O&m personnel plan server specifications, number, machine room deployment, node expansion and scaling. This era is the Serverfull era.

Then came the DevOps era. In this era, operation and maintenance developed a set of operation and maintenance console by themselves, which allowed students to conduct service observation, data query, operation and maintenance processing, etc., on the console. The work of operation and maintenance students was much easier, and the manpower of operation and maintenance students was mainly released at this stage.

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 was the Serverless era.

How do you use the Serverless

You’ve probably had the experience of writing code on the left side of a Web interface and showing execution on the right.

  • It’s blocks of code, not a lot of code;
  • Code runs fast;
  • Support for multiple programming languages;
  • Can support unpredictable flow peak impact.

Ali Cloud solution to see how to support multi-language architecture:

Abstractly, the front end simply passes code snippets and programming language identifiers to the Server and waits for the response. The Server can perform runtime classification and preprocessing for different programming languages.

How do Serverless

Many people regard Serverless as function compute (FC). Using function computing, the business does not need to build its OWN IT infrastructure, only needs to code and upload the code. Function computing prepares computing resources for you on demand, runs flexibly and reliably, and provides governance capabilities such as trace, log query, and alarm monitoring.

Such as:

In FC, there are services and functions. A service can contain multiple functions. We can use microservices to understand that we have built a microservice architecture through Golang or Java, and the FC service is the class, and the FC function is a method in the class:

The difference is that Java microservices can only run Java class code, golang class can only run the code written by GO, while FC functions can install runtime of different languages, supporting the running of programs of different languages.

After understanding the analogy, let’s look at how to call FC functions. Common FC solutions have the concept of a trigger. Such as HTTP trigger, object storage trigger, log service trigger, scheduled task trigger, CDN trigger, message queue trigger, and so on. A trigger is an abstract closure for an FC function call. For example, an HTTP trigger is usually analogous to an HTTP request event on a gateway, or an image is uploaded to a specified object storage path. The entry of these trigger events can be a trigger.

After the trigger generates the event, the FC function can be called. The logic of the function can be to download an image or register a user.

This is an FC lifecycle from trigger to FC function logic processing.

So how does FC achieve high availability?

In fact, the underlying code of each function runs on a set of IaaS platform. Using IaaS resources, we can set the memory configuration required by the running code for each function, such as the minimum 128M, the maximum 3G, etc. Developers don’t need to care what server the code is running on, how many function instances are started to support the current scenario, or the elastic scaling behind it, all converging behind FC.

There are two high availability policies in the figure:

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

Similar to thread pools.

So how does Serverless improve performance?

  • High efficiency: if a new language is added, you only need to create a corresponding FC function of the Runtime.
  • High availability: Ensures high availability through multi-threading and multi-instance. In addition, the expansion and contraction of function instances are handled by FC itself without any o&M configuration.
  • Low cost: Without trigger requests, function instances are not pulled or charged, so the cost of FC consumption is very low during off-peak traffic or at night.

How to create an FC in the cloud platform

1. Create a service

  • First create a new service name;
  • Select the area where the service will be deployed (help you deploy it in the nearest target machine room behind you);
  • Select whether to enable debug logging (enabled during development, but disabled when running online).

2. Create functions

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

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

For example, if you select an HTTP trigger and bind the function name to the trigger, you can select HTTP access authentication, authentication mode, and request mode POST or GET.

3. Code writing

When the function is created, enter the function and you can see the description, code execution history, trigger type, log query page, and so on. For HTTP triggers, you need to configure the HTTP trigger path.

As you can see, like a function in a class, the context request is 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): Request_body_size = 0 # Obtain code from environ['wsgi.input']. Read (request_body_size) codeStr = Urllib.parse. Unquote (request_body.decode("GBK")) # CodeArr = codestr.split ('&') code = codeArr[0][5:] inputStr = codeArr[1][6:] # Save user code as py file, / TMP directory, FileName = '/ TMP /' + STR (int(time.time())) + '.py' f = open(fileName, F.write ('import time \r\n') f = open(fileName, "a") f.write(code) f.close() P =subprocess.PIPE ("python "+ fileName, stdout=subprocess.PIPE, stdin=subprocess.PIPE, Stderr =subprocess.PIPE, shell=True, encoding=' UTF-8 ') # Input user input via standard input if inputStr! = ' ': Write (inputStr + "\n") p.scheddin.flush () r = p.schedout.read () status = '200 OK' response_headers =  [('Content-type', 'text/plain')] start_response(status, response_headers) return [r.encode('UTF-8')]Copy the code

The process is as follows:

  • The front-end passes in snippets of code in the form of strings;
  • Get the code string passed in the FC function, intercept the code content and input content;
  • Save the code as a py file, named with a timestamp, in the/TMP directory of the FC function. Each function has its own/TMP directory.
  • Import time library code;
  • Create a subprocess using the subprocess. Run the py command in shell mode to execute the py file saved in the/TMP directory.
  • Finally read the execution results back to the front end.

Front-end call FC function:

The whole process only needs the front-end to pass the code into the FC function, the whole Server side of each link does not need r & D and operation and maintenance students care, embodies the essence of Serverless.

Coordinate workflow with Serverless

Workflow can arrange the execution of tasks in order, branch, parallel and other ways, and then the process will reliably coordinate the execution of tasks according to the set steps, track the state transition of each task, and execute the defined retry logic when necessary to ensure the smooth execution of the process.

Workflow processes monitor workflow execution by logging and auditing, facilitating process diagnosis and debugging.

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

Services that require flexible workflow configuration

For example, in the catering scene, different merchants can configure different payment methods, such as wechat Pay, UnionPay and Alipay. You can support three at the same time, you can also one, you can collect, you can also exchange points, etc. Without a good configuration process solution, a large number of hard-coded rule judgments can occur in the system, and the system iteration is a tiring and unsustainable process.

With the workflow built by FC, this problem can be solved elegantly. For example, the process is as follows:

The above flow is user-side flow, and then needs to be converted to application-side flow to create the workflow through constrained FDL, as shown in figure:

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
Copy the code

The example illustrates the flexible workflow that FC can implement based on Serverless.

How is the process triggered?

After the user has selected the item and filled in the address, the process can be automatically triggered by pulling the item and order context.

In the context of microservices, many capabilities are not closed loop within the logic of single code, and are often connected to multiple business systems, for example, the whole process of connecting multiple OpenAPI interfaces in series:

If you want to use the process engine, you need to carry out 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"); // RAM account Access Key Secret IAcsClient client = new DefaultAcsClient(profile); return client; }}Copy the code

How to concatenate processes in startFNF code:

  • 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);
    }
Copy the code

Look again 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);
    }
Copy the code
  • The first part is the startup process;
  • The second part is to create order pairs and simulate warehousing.

How does the front end call?

In the front end, after clicking the next step in the page of selecting goods and merchants, the HTTP interface /startFNF/{fnfName}/{execuname}/{input} is invoked through GET. Corresponds to the Java method above.

  • Fnfname: the name of the process to start;
  • Execuname: Randomly generate uuid as the order number and as the name of the startup process instance;
  • Input: The product, merchant, order number, and address are constructed as JSON strings and passed 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) } }) }Copy the code

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
Copy the code
  • Name: indicates the node name.
  • TimeoutSeconds: Specifies the timeout period for a node to wait before jumping to an orderCanceled node pointed to by the Goto branch.
  • Pattern: Set to waitForCallback to wait for confirmation;
  • InputMappings: The connection parameters of this node;
    • TaskToken: Serverless Token automatically generated by workflow;
    • A. products B. products C. products D. products
    • Supplier: selected business;
    • Address: a delivery address.
    • OrderNum: Order number;
  • OutputMappings: The outreaching parameters of this node;
    • Paymentcombination: payment method supported by the merchant
    • OrderNum: Order number;
  • Catch: To catch an exception and jump to another branch.

Serverless workflow supports the integration of multiple cloud services, with other services as execution units of task steps. Service integration is realized by FDL expression. In task steps, resourceArn can be used to define integrated target services, and pattern can be used to define integration patterns.

Configure /topics/ Generateinfo-fnF-demo-jiyuan /messages in resourceArn to integrate MNS message queue service When generateInfo is triggered, a message is sent to GenerateInfo-fnF-Demo-Jiyuantopic. The body and parameters of the message are specified by Zhi’d in the serviceParams object. The MessageBody is the MessageBody, and the $configuration indicates that the MessageBody is generated through the input mapping inputMappings.

GenerateInfo FNF – demo function:

The message sent to GenerateInfo-FNF-Demo-Jiyuantopic contains the product information, merchant information, address, and order number, indicating the beginning of an order placing process. Since the message is sent, the message must be received for subsequent processing. In the function computing console, create the service, and under the service create the event trigger function named Generateinfo-fnF-demo. Select Python Runtime:

Create MNS trigger, 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. 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 event is received in Topic GenerateInfo-fnF-demo-jiyuan. 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 determine which merchant is using which combination of payment methods, the example here is simple and crude. Normally, you should use metadata configuration to obtain paymentCombination = "" if supplier == "haidilao": paymentcombination = "zhifubao,weixin" else: paymentcombination = "zhifubao,weixin,unionpay" # 4. Call the Java service exposed interface, update the order information, Mainly is to update the payment url = "http://xx.xx.xx.xx:8080/setPaymentCombination/" + + "/" orderNum + paymentcombination + x = "/ 0" requests.get(url) # 5. 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'Copy the code

The code is divided into five parts:

  • Build Serverless workflow Client;

  • The information in the event is that the message content in topicGenerateInfo-fnF-Demo-Jiyuan is received and converted into Json objects.

  • To determine which merchants use which combination of payment methods, the example here is simple and crude, and normally it should be obtained using metadata configuration. 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 for query.

  • Call the Java service exposed interface, update the order information, mainly update the payment method;

  • Give the generateInfo node a response and return the data, which returns the order number and payment method. Because the pattern of this object is waitForCallback, you need to wait for the response result.

The generateinfo-fnF-demo function is configured with the MNS trigger. When topicGenerateinfo-fnf-demo-jiyuan receives a message, the generateinfo-fnF-demo function is triggered.

2. Payment node

Here 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
Copy the code

When the process flows to the Payment node, the user can go to the payment page.

The Payment node sends a message to the MNS topicPayment-fnF-demo-jiyuan, which triggers the payment-FnF-demo function.

Payment – FNF – demo function:

The payment-fnF-demo function is created in a similar manner to generateinfo-fnF-demo.

# -*- 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'
Copy the code

The core idea of the above code is to wait for the user to select a payment method on the payment page to confirm payment. MNS queues are used to simulate waiting. It waits to receive messages in the payment-queue-fnF-demo, and returns the order number, payment method and amount selected by the user to the Payment node after receiving the message.

Front end selection of payment method page:

After passing through the generateInfo node, the payment method information of the order is already available, so for the user, after filling in the product, merchant and address, the page to jump to is the confirmation payment page, and includes the payment method supported by the merchant.

After entering the page, the interface exposed by the Java service is requested to obtain the order information and display different payment methods according to the payment method on the page.

The code snippet is as follows:

When the user selects a payment method and clicks the submit order button, a message is sent to the payer-queue-fnF-Demo queue to inform the payer-FnF-Demo function to continue with the subsequent logic.

Paymentmethod-fnf-demo: paymentMethod-fnF-demo: paymentMethod-fnF-demo:

# -*- 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]Copy the code

The logic of the function is simple: it sends the payment method and amount selected by the user to the MNS queue payment-queue-fnF-demo.

3. PaymentCombination node

A paymentCombination node is a routing node that routes to different nodes by determining a parameter, using paymentMethod 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
Copy the code

The process is that the user selects the payment method, sends the message to the payment-FnF-Demo function, and then returns the payment method, which then flows to the paymentCombination nodes and functions that process the payment logic by determining the payment method.

4. The 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
Copy the code

The resourceArn of the node is different from that of the previous two nodes. Here, the ARN of the function in function calculation is configured, that is, when the process flows to this node, the zhifubao-FnF-demo function is triggered, which is an event triggering function without creating any triggers. The process passes the order amount, order number, and payment method to the zhifubao-FSF-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"}Copy the code

The code logic is simple, after receiving the amount, take 20% off the amount and then update the price back to the order. The nodes and functions of other payment methods do the same and change the implementation logic. In this example, wechat Pay is 50% off and UnionPay is 30% off.

Complete the process

The orderCompleted and orderCanceled nodes in the flow do no logic. The flow is as follows:

The node flow seen from the Serverless workflow looks like this:

Write in the back

The above is a workflow based on FC implementation of Serverless. The simulation builds an order module, and the rules include:

  • Configure metadata rules for merchants and payment methods;
  • Confirm the metadata rules for the payment page.

In actual projects, the customizable part needs to be abstracted into metadata description, and there needs to be a configuration interface for the operation or business to customize the payment method, namely, metadata rules, and then the front and back end pages display the corresponding content based on metadata information.

If you need to access a new payment method later, you only need to determine the routing rule in the paymentCombination routing node and then add the corresponding payment method function. By adding the metadata configuration item, the new payment method can be displayed on the page and routed to the new payment function.

After the whole article, I believe that many people have a certain understanding of the definition of Serverless and how to realize business capabilities based on the Serverless function of the existing public cloud system. Even based on this, powerful companies can develop a set of Serverless platform. Of course, the idea is the same. In fact, a lot of logic and theory in this paper are not only applicable to Serverless, but also to our daily platform/platform solutions based on micro-services, from which we can obtain design nutrition and apply it in work.