This topic describes how to write a function that is assigned a public IP address in the Function Compute console.

Background information

In this topic, function B represents the third-party application B with RAM, and function A represents the application A that needs to access the Function Compute of third-party application B.

Procedure

  1. Log on to the Function Compute console.
  2. In the left-side navigation pane, click Services and Functions.
  3. In the top navigation bar, select the region where the service resides.
  4. Create Function A.
    1. On the Services page, click the target service.
    2. On the Functions page, click Create Function.
    3. On the Create Function page, select Create from scratch. In the Basic Settings section, enter the function information and click Create.
      function-a
      Parameter Required Operation Example
      Parameter No The name of the function. It must start with a letter and can contain digits, letters (case-sensitive), underscores (_), and hyphens (-). It can be up to 64 characters in length.
      Note If you do not specify a name, the Function Compute is automatically created for you.
      Function-A
      Runtime environment Yes Select a language, such as Python, Java, PHP, or Node.js. For more information about the runtime environments supported by Function Compute, see . Python 3
      Function trigger mode Yes Select a function trigger mode.
      • Trigger by Event: You can use a timer or a trigger of another Alibaba Cloud service to trigger function execution.
      • Triggered by HTTP Request: You can send an HTTP request to trigger function execution, which is suitable for scenarios such as quickly building Web services.
      Triggered by HTTP request
      Instance Type Yes The instance category for the function. Valid values:
      • Elastic Instance
      • Performance Instance
      For more information, see Instance specifications and usage modes.
      Elastic instance
      Memory specification Yes The memory that is required to execute the function.
      • Select Input: Select the required memory from the drop-down list.
      • Manual Input: Click Manually Enter Memory Size to customize the memory size of the function. The memory specifications are described as follows:
        • Elastic Instance: Valid values: [128, 3072]. Unit: MB.
        • Performance Instances: Valid values: [4 to 32]. Unit: GB.
        Note The memory value must be a multiple of 64 MB.
      512 MB
    4. On the Function Code tab, enter the following code in the code editor.
      -*- coding: utf-8 -*-
      import logging
      import urllib, urllib2, sys
      import ssl
      import json
      # The IP address of the proxy server. 
      proxy_address = 'ip:port'
      # The endpoint of the third-party service that is implemented by Function B. 
      data_service_host = 'https://{id}.{region}.fc.aliyuncs.com'
      data_service_path = '/service/path'
      def handler(environ, start_response):
          """
          entrance
          """
          url = data_service_host + data_service_path
          # Use the proxy to access the third-party service. 
          proxy_result = get_data_by_url(url, proxy_address)
          # Do not use the proxy to access the third-party service. 
          normal_result = get_data_by_url(url, None)
          # Merge and display the returned results in the two cases. 
          result = {
              "query_with_proxy_result": proxy_result,
              "query_without_proxy_result": normal_result
          }
          status = '200 OK'
          response_headers = [('Content-type', 'text/json')]
          start_response(status, response_headers)
          return json.dumps(result)
      def get_data_by_url(url, proxy):
          """
          Encapsulate the service that accesses user data. 
          """
          result = {
              "success": False,
              "secret_data": '',
              "data_service_raw_data": {}
          }
          content = my_http_request(url, proxy)
          if content:
            content = json.loads(content)
          result["data_service_raw_data"] = content
          # Process data after simulated access. 
          if "authorized" in content and content["authorized"]:
              result["success"] = True
              result["secret_data"] = content["protected_data"]
          return result
      def my_http_request(url, proxy=None):
          """
          The network request that uses the proxy. 
          """
          try:
              ret = None
              socket_fd = None
              request = urllib2.Request(url)
              request.add_header('User-Agent', 'Fake_browser/1.0')
              if proxy:
                  request.set_proxy(proxy, 'http')
              opener = urllib2.build_opener()
              socket_fd = opener.open(request)
              ret = socket_fd.read()
          except Exception as e:
              ret = json.dumps({"info": "exception in proxy query: %s" % e})
          finally:
              if socket_fd:
                  socket_fd.close()
          return ret          

      The following part describes the related functions:

      • handler is a function that Function Compute can invoke when Function Compute executes the code. It simulates the results of invoking Function B with and without using the proxy.
      • get_data_by_url contains the logic for invoking Function B.
      • my_http_request sends HTTP requests by using the proxy.
  5. Repeat the preceding steps to create Function B. On the Function Code tab, enter the following code in the code editor.
    Note Function B uses the Django framework. Therefore, you can use the command-line tool Serverless Devs or Python SDK to upload code packages. You can also use OSS or ZIP packages to upload code packages directly.

    Function B has the following code structure:

    project/
    ├── lib
    │   ├── Django-1.11.13.dist-info
    │   ├── django
    │   ├── pytz
    │   └── pytz-2018.4.dist-info
    ├── main.py
    └── src
        ├── __init__.py
        ├── bin
        │   ├── __init__.py
        │   └── manage.py
        ├── conf
        │   ├── __init__.py
        │   ├── settings.py
        │   ├── urls.py
        │   └── wsgi.py
        ├── data
        └── views
              ├── __init__.py
              └── view.py            

    The following example shows the Django code:

    • Function handler:
      #!/usr/bin/env python
      # coding=utf-8
      import sys
      import os
      # load local django
      sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)) + '/lib')
      sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), "src"))
      import django
      print (django.__version__)
      import views.view
      from conf.wsgi import application
      def handler(environ, start_response):
          import urlparse
          parsed_tuple = urlparse.urlparse(environ['fc.request_uri'])
          li = parsed_tuple.path.split('/')
          global base_path
          if not views.view.base_path:
              views.view.base_path = "/".join(li[0:5])
          return application(environ, start_response)            
    • urls.py:
      from django.conf.urls import url
      from django.contrib import admin
      from views import view
      urlpatterns = [
          url(r'^index$', view.index),
          url(r'^get_data$', view.get_data),
      ]           
    • views.py:
      #!/usr/bin/env python
      # coding=utf-8
      from django.http import HttpResponse
      from django.conf import settings
      import logging
      import json
      logger = logging.getLogger()
      base_path = ""
      def index(request):
          """
          Test the handler. 
          """
          logger.info("Django request detected! url: index")
          white_list = settings.WHITE_LIST
          allowed_hostlist = ' '
      
          for allowed_host in white_list:
              allowed_hostlist += allowed_host
              allowed_hostlist += ' '
          return HttpResponse("<h1>It works! Copyright: jianyi</h1> White list: %s" % allowed_hostlist, status=200)
      def get_data(request):
          """
          Obtain the data. 
          """
          result = {
              "remote_ip": '',
              "white_list": [],
              "authorized": False,
              "protected_data": ''
          }
          if request.META.has_key('HTTP_X_FORWARDED_FOR'):
              remote_ip = request.META['HTTP_X_FORWARDED_FOR']
          else:
              remote_ip = request.META['REMOTE_ADDR']
      
          result["remote_ip"] = remote_ip
          result["white_list"] = settings.WHITE_LIST
      
          if remote_ip in result["white_list"]:
              result["authorized"] = True
              result["protected_data"] = "Alibaba"
      
          return HttpResponse(json.dumps(result), status=200)           
    • settings.py: Add the whitelist setting to the end of the default settings.py file, and add the public IP address of the proxy server to the whitelist.
      # User conf
      WHITE_LIST = [
          "127.0.0.1",
          "xx.xx.xx.xx"
      ]           
  6. Build and start an NGINX proxy.

    You can deploy NGINX on an Elastic Compute Service (ECS) instance or a normal server. For more information, visit Install NGINX. After you install NGINX, you can configure the proxy settings in the configuration file based on the following sample code:

    server{
        resolver x.x.x.x;
        listen 8080;
        location / {
            proxy_pass http://$http_host$request_uri;
        }
    }           
    Notice
    • The proxy settings in the configuration file cannot contain the hostname parameter.
    • The resolver must be set to the IP address of your DNS server.
    • Do not change NGINX system variables $http_host and $request_uri.
    • In the actual production environment, we recommend that you build SLB clusters (such as Nginx and Keepalived) and proxy service clusters for proxy service to improve system availability and overall performance.

What to do next

Debug a function