【AWS征文】带你使用 AWS 无服务器架构一步步打造个性化 API 接口

没有 web 接口开发经验,只会简单写一写功能函数,有没有办法写一个收集客户端数据的接口呢?本文将一步步带你如何在没有接口开发经验的状况下轻松的使用 AWS 服务器服务构建本身定制化的 API 接口。html

1、前期准备

1.一、业务需求

假设 T 公司有一个全球性的网站,每一个国家站点都有一个下载页面。公司想要去监测全球用户的下载状况,须要对下载按钮进行埋点,那咱们就须要有一个接口能够监测到用户的下载状况,须要记录的数据有以下:python

  • country:用户来自哪一个国家
  • create_time:下载时间
  • ip:用户的 IP
  • referer:从哪一个页面下载的
  • site:国家站点代码

咱们不可能针对每一个站点都作一个连接做为借口,最好的方式就是建立一个连接,经过传递不一样的参数来埋到不一样的下载按钮,连接相似下面结构:linux

https://down.wzlinux.com?type=CN
https://down.wzlinux.com?type=US
https://down.wzlinux.com?type=IN

1.二、解决方案

看到这个需求,一个专业的开发人员可能很快能够解决,须要较高的开发技能,还须要运维购买服务器,部署等,相对来讲比较麻烦,而且管理起来相对复杂。本文所展现的就是你只会简单的功能函数开发便可完成这个需求的开发部署,并且所有使用 AWS 的无服务器组件,用户不须要再关系底层硬件,来多少请求量咱们就花多少钱,也能够节省没必要要的支出。web

咱们知道在 web 里面重要的一个组合 LAMP(Linux + Apache + MySQL + PHP)。在 AWS 中呢,也有一个无服务器架构组合 Lambda + API Gateway + DynamoDB,这个组合正好知足咱们的需求。API Gateway 用来接收用户的请求数据,是实现 web api 的重要组件,Lambda 用来实现逻辑处理,选择用户须要的数据,DynamoDB 用来存储 Lambda 的数据。数据库

image-20200807174608309

2、部署

整个架构有一些依赖关系,Lambda 依赖 DynamoDB,API Gateway 依赖 Lambda,因此咱们先去建立 DynamoDB 表,而后去建立 Lambda 函数,最后建立 API Gateway。json

全部的操做均选择在 AWS us-east-1 区域。api

2.一、建立 DynamoDB

建立 DynamoDB 很是简单,在 Console 简单点几下就能够,由于 DynamoDB 须要一个主键,咱们把生成的 uuid 做为主键,填写完成建立便可。安全

image-20200807175509048

是否是很简单,就这样建立好了,若是你使用 awscli 建立的话,那更简单:bash

aws dynamodb create-table \
    --table-name wzlinux-down \
    --attribute-definitions AttributeName=id,AttributeType=S \
    --key-schema AttributeName=id,KeyType=HASH \
    --billing-mode PAY_PER_REQUEST \
    --region us-east-1

就这样简单的建立好了,下面咱们开始建立比较重要的 Lambda。服务器

2.二、建立 Lambda

建立角色权限

由于 Lambda 须要有权限向 DynamoDB 写入数据,咱们能够本身建立一个 Lambda 角色,赋予对呀的权限,在建立的时候,把咱们所建立的 Role 赋予 Lambda,我这里给的资源范围大了一些,安全要求高的,能够给予最小的权限,权限 json 以下:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "logs:CreateLogGroup",
                "dynamodb:PutItem",
                "dynamodb:Scan",
                "dynamodb:Query",
                "dynamodb:UpdateItem"
            ],
            "Resource": "*"
        }
    ]
}

console 建立

建立 Lambda 也比较简单,大部分参数选择默认便可,注意在 Permissions 里面选择咱们上面建立好的 Role,运行环境咱们选择 Python,固然 Lambda 也支持 .NET、Go、Java、Node.js、Ruby,甚至还支持用户自定义运行环境。

image-20200807181426650

制做代码包

我这里已经把 Lambda 函数写好了,文件命名为lambda_function.py

import boto3
import os
import uuid
import time
from requests import get

def lambda_handler(event, context):
    recordId = str(uuid.uuid4())
    ctime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())

    sourceIP = event["headers"]["x-forwarded-for"]
    url = "https://api.ipdata.co/{0}/country_name?api-key=40b58339be0ec3ba64fb936da37dbab9c9d1677f335c50910c52aeb7".format(sourceIP)
    country = get(url).text
    try:
        referer = event["headers"]["referer"]
    except:
        referer = "NULL"

    try:
        site = event["queryStringParameters"]["type"]
    except:
        site = "NULL"

    # Createing new record in DynamoDB table
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table(os.environ['DB_TABLE_NAME'])
    table.put_item(
        Item={
            'id': recordId,
            'site': site,
            'create_time': ctime,
            'ip': sourceIP,
            'country': country,
            'referer' : referer
        }
    )
    return {"statusCode": 200, "body": "OK"}

大体介绍一下函数,咱们从 API Gateway 传入的参数获取用户来源 IP,而后经过一个接口把 IP 转换为国家。

再一个就是获取连接从哪一个页面请求过来的,还有一个就是给我上面传入的参数 type 的取值。

Lambda 获取的参数主要经过 event 传递进来,event 是 API Gateway 传过来的参数,咱们使用最简单的 HTTP API,我这边记录了传过来的 event,详细信息能够参照官方文档,这样你们就能够更加理解 Lambda 函数是怎么截取的数据。

{
    "version": "2.0",
    "routeKey": "GET /",
    "rawPath": "/",
    "rawQueryString": "type=CN",
    "headers": {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "accept-encoding": "gzip, deflate, br",
        "accept-language": "zh-CN,zh;q=0.9",
        "content-length": "0",
        "host": "49imv2pyz1.execute-api.us-east-1.amazonaws.com",
        "sec-fetch-dest": "document",
        "sec-fetch-mode": "navigate",
        "sec-fetch-site": "none",
        "sec-fetch-user": "?1",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36",
        "x-amzn-trace-id": "Root=1-5f2f6066-93614fb0bb62a21428cb8508",
        "x-forwarded-for": "52.221.86.56",
        "x-forwarded-port": "443",
        "x-forwarded-proto": "https"
    },
    "queryStringParameters": {
        "type": "CN"
    },
    "requestContext": {
        "accountId": "921283538843",
        "apiId": "49imv2pyz1",
        "domainName": "49imv2pyz1.execute-api.us-east-1.amazonaws.com",
        "domainPrefix": "49imv2pyz1",
        "http": {
            "method": "GET",
            "path": "/",
            "protocol": "HTTP/1.1",
            "sourceIp": "52.221.86.56",
            "userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"
        },
        "requestId": "Q-wAChzJoAMESlQ=",
        "routeKey": "GET /",
        "stage": "$default",
        "time": "09/Aug/2020:02:33:10 +0000",
        "timeEpoch": 1596940390575
    },
    "isBase64Encoded": "False"
}

由于 python 函数有依赖包,咱们须要把依赖包一并打包传到 Lambda,关于打包的过程以下,咱们这里用到的依赖包为 requests。

  1. 使用 pip--target 选项在新的项目本地 package 目录中安装库。
~/my-function$ pip install --target ./package requests
  1. 建立包含依赖项的 ZIP 存档。
~/my-function$ cd package
~/my-function/package$ zip -r9 ${OLDPWD}/wzlinux-down.zip .
  1. 将您的函数代码添加到存档中。
~/my-function/package$ cd $OLDPWD
~/my-function$ zip -g wzlinux-down.zip lambda_function.py

上传代码包

打包好的代码包wzlinux-down.zip能够直接传到 Lambda,也能够先传到 S3,再上传到 Lambda,上传完成以后以下:

image-20200809111714931

注意:由于咱们代码中有环境变量,就是咱们前面建立的 DynamoDB,在环境变量这一列建立好便可。

DB_TABLE_NAME:wzlinux-down

模拟测试

点击右上角的测试,咱们发送测试的 json,这里咱们就模拟 HTTP API,直接把上面的示例数据帖进去便可。

image-20200809112338953

选择执行,查看到已经执行成功了。

image-20200809112434981

而后再去查看 DynamoDB 中,是否已经有数据写入。

image-20200809112646453

至此,咱们的 Lambda 已经打通,一样附上使用 awscli 建立 Lambda 的命令,须要咱们提早把代码和依赖库打包上传到 S3。

aws lambda create-function \
    --function-name wzlinux-down \
    --runtime python3.8 \
    --role arn:aws:iam::921283538843:role/wzlinux-down-role \
    --handler lambda_function.lambda_handler \
    --code S3Bucket=code.wzlinux.com,S3Key=wzlinux-down.zip \
    --environment Variables={DB_TABLE_NAME=wzlinux-down} \
    --region us-east-1

--role:选择你建立好的 Role

--code:这里是我打包好的代码,上传到了个人 S3 中

2.三、建立 HTTP API

Console 建立

  1. 第一步,选择要集成的 Lambda 函数,已经 API 名称。

image-20200809113204675

  1. 第二步,设置路由,咱们这里就选择默认的根目录吧,方法选择 GET。

image-20200809113325886

  1. 第三步,阶段方面也就选择默认吧,毕竟咱们不会常常去发布新版本的 API。

image-20200809113518605

访问测试

建立好以后,咱们直接请求这个连接访问看看效果,固然咱们能够再连接后面传递参数 type,如请求地址:

https://jat553o58l.execute-api.us-east-1.amazonaws.com/?type=CN
https://jat553o58l.execute-api.us-east-1.amazonaws.com/?type=US

image-20200809113724110

一样查看一下 DynamoDB 数据库,查看数据是否入库:

image-20200809114018289

自定义域名

为了方便记录,咱们可使用本身的域名,在 Custom domain names 里面进行建立。

image-20200809115454420

建立好以后,咱们为域名添加 CNAME 解析,解析值为 API Gateway domain name。

image-20200809115848616

解析完成以后,咱们须要为自定义域名配置一个 API mapping。

image-20200809210604715

image-20200809210639796

而后咱们就可使用咱们最开始规划的地址进行访问了。

https://down.wzlinux.com?type=CN
https://down.wzlinux.com?type=US
https://down.wzlinux.com?type=IN

咱们经过一个 URL,传递不一样的参数,为各个国家生成了不一样的接口,只须要埋到对应的页面,当用户访问的时候,咱们就会捕获到数据,进而能够进行分析。

3、总结

AWS 这一套服务器架构(API Gateway + Lambda + DynamoDB),极大的减轻了咱们开发 API 接口的工做量,使得只是简单懂得处理逻辑的函数开发人员便可完成 API 的开发,而且无服务器架构还有不少传统应用不具备的优点,能够预见无服务器架构会成为云计算的下一个纪元:

  • 便宜:只有使用的时候才须要付费
  • 可扩展:根据负载动态扩展
  • 下降运维:不须要维护底层硬件和系统
  • 用户体验:容许公司投入更多时间和资源来开发和改进面向客户的功能。

在第一次配置调试过程当中,注定会遇到各式各样的问题,你们必定要充分利用 CloudWatch Logs 这个服务,咱们的不少报错均可以在里面找到,最后,若是你们都已经学会了,那就开启你的无服务之旅吧,充分利用云原生的一些功能,把咱们的应用进行现代化。

欢迎你们扫码关注,获取更多信息

【AWS征文】带你使用 AWS 无服务器架构一步步打造个性化 API 接口