与Cloudflare工人和无服务器Redis在边缘限制的费率限制
#serverless #redis #cloudflare #edge

在本教程中,我们将展示如何使用CloudFlare工人和Upstash Redis限制您的应用程序。我们将使用将数据保存在upstash redis中的rate limiting SDK

REDIS设​​置

使用Upstash ConsoleUpstash CLI创建一个或多个Redis数据库。请勿选择全局,而是在您期望进入网站流量的不同区域中创建多个数据库。 (为什么?我们稍后会解释)您将需要在接下来的步骤中进行剩下的URL和令牌。

项目设置

我们将使用Wrangler 2进行部署,因此请安装(或升级)Wrangler 2

为您的项目创建一个文件夹并运行wrangler init。选择ts

 ⛅️ wrangler 2.0.7 (update available 2.0.26)
------------------------------------------------------
Using npm as package manager.
✨ Created wrangler.toml
No package.json found. Would you like to create one? (y/n)
✨ Created package.json
Would you like to use TypeScript? (y/n)
✨ Created tsconfig.json
Would you like to create a Worker at src/index.ts? (y/n)
✨ Created src/index.ts

编码

更新工人的功能如下:

import { Redis } from "@upstash/redis/cloudflare";
import { MultiRegionRatelimit } from "@upstash/ratelimit";

export interface Env {
}

const cache = new Map();

export default {
  async fetch(
    request: Request,
    env: Env,
    _ctx: ExecutionContext,
  ): Promise<Response> {
    if (new URL(request.url).pathname == "/favicon.ico") {
      return new Response(null, { status: 400 });
    }

    const ratelimit = new MultiRegionRatelimit({
      redis: [
        new Redis({
          url: "https://us1-cool-macaque-32148.upstash.io",
          token: "AASQgODM5ZjExZGEtMZDRjOWI4ZmVjNmE3ZDk3NDIxMmEwNmNkYjVmOGVmZTk5Mz",
        }),
        new Redis({
          url: "https://eu2-merry-caribou-31549.upstash.io",
          token: "AXe5ASQgNmM5YmFiYWEtMmV3N2E1ODFiMWExMDc5NNGFiYWEwMzk2ZjE4ZWQ3N2Y",
        })
      ],
      limiter: MultiRegionRatelimit.fixedWindow(5, "5 s"),
      ephermeralCache: cache,
    });

    const userIP: string = request.headers.get('CF-Connecting-IP') || "none";

    const data = await ratelimit.limit(userIP);
    if (data.success) {
      // if you use CF Workers to intercept another site, uncomment below
      // return await fetch(request);
      return new Response(JSON.stringify({ message: "Success, you are not rate limited.", ip: userIP, data }, null, 2), { status: 200 });
    } else {
      // show an error page for rate limited users
      return new Response(JSON.stringify({ message: "You are rate limited, try again later.", ip: userIP,  data }, null, 2), { status: 200 });
    }
  },
};

替换上述redis实例的REST_URL和令牌。您可以根据流量在不同区域(或设置单个区域)中添加更多REDIS数据库。

测试和部署

您可以使用wrangler dev进行本地测试。

wrangler publish

将功能部署到CloudFlare

将打印函数的端点。例如https://cloudflare-workers-rate-limiting.upsdev.workers.dev/当您连续5次刷新页面时,您应该看到您的费率有限。

好吧,教程结束了(很容易,对吗?),现在让我们介绍一些细节。

为每个请求远程通话不是昂贵的吗?

可能是我们不使用短暂的缓存。 The ephemeral cache是在处理程序外定义的地图对象。限制SDK的速率使用它在无服务器函数处于活动状态时缓存标识符(热)。在处理程序外定义它很重要,因此它将是一个全局变量。 CloudFlare工人不能保证不同的调用将使用相同的变量,但在高峰流量期间仍然有很大的帮助。

为什么不全局数据库?

UpStash全局数据库将重新数据复制到多个区域。它非常适合许多边缘用例。但是对于限制速率,我们不需要将一个区域的数据复制到所有其他区域。费率限制会议具有本地范围。我们仍然可以使用全球数据库,但是这将是不必要的昂贵的,因为每个更新都会复制给全世界。这就是为什么我们选择创建多个区域数据库为the rate-limiting SDK recommends

IP作为标识符

我们已将用户的IP用作限制函数的标识符。这意味着我们限制了单个用户的速率。如果您的请求中有更好的标识符(用户名,电子邮件等),请使用它。如果要限制总共流量,则可以设置一个常数字符串。或者,您可以使用区域或国家(request.cf.country)作为标识符来限制每个地理的流量。

我可以使用自托管的redis吗?

CloudFlare工人在V8隔离株上运行,并且不允许T​​CP连接。因此,除非您使用REST代理,否则您无法访问自托管的redis,redislabs或Elasticache。 Upstash Redis具有内置的REST API。

集成到您现有网站

您可以在不触摸其代码的情况下限制现有的Web应用程序/站点。您只需要在CloudFlare中创建一个站点,并相应地为您的域名设置名称服务器。因此,CloudFlare将能够拦截您网站的请求并运行工人功能。也要输入XX行,因此您的工人功能将使请求转发到您的网站,如果请求不得限制。