使用 Amazon S3 和 Cloudfront 智能缓存网页

Use Amazon S3 and Cloudfront for intelligently caching webapges

我有一个网站(在 Elastic Beanstalk 上 Tomcat 内 运行)生成艺术家唱片目录(一个艺术家的单个页面)。这可能会占用大量资源,因此艺术家页面在一个月内不会发生变化,因此我在其前面放置了 CloudFront Distribution。

我认为这意味着我的服务器不必多次处理任何艺术家请求,但它并没有那么好。 post 解释了每个边缘位置(欧洲、美国等)在第一次查找资源时都会错过,并且云端缓存中保留的资源数量是有限的,因此它们可能掉线了。

因此,为了解决这个问题,我更改了服务器代码,将网页的副本存储在 S3 中的存储桶中,并在收到请求时首先检查这一点,因此如果艺术家页面已存在于 S3 中,则服务器检索它并 return 将其内容作为网页。这大大减少了处理,因为它只为特定艺术家构建一次网页。

但是:

  1. 请求仍然需要去服务器检查艺术家页面是否存在。
  2. 如果艺术家页面存在,则首先将网页(它们有时最大可达 20mb)下载到服务器,然后服务器 return 页面。

所以我想知道我是否可以对此进行改进 - 我知道您可以构建一个 S3 存储桶作为到另一个网站的重定向。有没有一种每页的方式我可以让艺术家请求转到 S3 存储桶,然后将它 return 页面存在,或者如果不存在则调用服务器?

或者,我可以让服务器检查页面是否存在,然后重定向到 S3 页面,而不是先将页面下载到服务器吗?

CloudFront 缓存重定向,但不遵循它:http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html#ResponseCustomRedirects

您没有提供具体的数字,但是您是否可以预生成所有这些页面并将它们放入 S3 并将 CloudFront 直接指向 S3?

如果可行,有两个好处:

  1. 您会将内容生成与内容服务分离,这将使系统总体上更加稳定
  2. 内容生成器的性能要求将低得多,因为它可以按照它希望重新生成内容的速度移动

当然,如果您不知道必须提前生成哪些页面,那将无法正常工作。

虽然我以前没有做过,但这将是我会看的一种技术。

  • 首先按照您的描述设置 S3 存储桶,作为网站的 "redirect"。

  • 查看 S3 事件处理程序。它们仅在创建 S3 对象时处理,但您可以尝试执行 GET 开始,如果它失败响应 POST 或 PUT 到同一路径,放置在 "marker" 文件中或调用将触发事件的 API?

https://aws.amazon.com/blogs/aws/s3-event-notification/ http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html

  • 一旦事件被触发,要么让您的服务器通过 SQS 侦听事件,要么将您的艺术家创作者代码移至 AWS Lambda 中,它将从 SNS 获取数据。

我唯一关心的是 GET 的来源。您不希望任何人使用无效的 POST 访问您的 S3 存储桶 - 您会到处生成。但我会把它留作 reader.

的练习

为什么不在tomcat前面放一个像ngx或者apache这样的web服务器呢?意味着 tomat 在其他端口(如 8085)上运行,Web 服务器在 80 上运行。它会被点击并拥有自己的缓存。那么你根本不需要 S3,但可以回到你的服务器 + Cloudfront。

所以 Cloudfront 访问您的 Web 服务器,如果它在缓存中,return 页面直接访问。否则转到 tomcat。

缓存可以在同一个进程或一个 redis 中...取决于您需要缓存的数据的总大小。

OP 说:

they can sometimes be large up-to 20mb

由于您提供的数据量可能非常大,我认为您可以在 2 个请求中而不是一个请求中执行此操作,这样您就可以将内容生成与内容服务部分分离。这样做的原因是为了最大限度地减少服务器从 S3 获取数据并提供服务所需的 time/resources 量。

AWS 支持pre-signed URLs,可以在短时间内有效;我们可以在这里尝试使用相同的方法来避免安全等方面的问题。

目前,您的架构如下所示,其中。客户端发起请求,你检查请求的数据是否存在于S3上,如果存在则获取并提供它,否则你生成内容,并将其保存到S3:

                           if exists on S3
client --------> server --------------------> fetch from s3 and serve
                    |
                    |else
                    |------> generate content -------> save to S3 and serve

在网络资源方面,您总是在这里消耗 2 倍的带宽和时间。如果数据存在,那么一旦您必须将其从服务器中拉出并将其提供给客户(所以它是 2X)。如果数据不存在,您将其发送给客户和 S3(所以还是 2X)


相反,您可以尝试下面的 2 种方法,这两种方法都假设您有一些基本模板,并且可以通过 AJAX 调用获取其他数据,并且这两种方法都会降低 2X 系数在整体架构中。

  1. 仅提供来自 S3 的内容。 这需要更改您的产品设计方式,因此可能不容易集成。

    基本上,对于每个传入请求,如果数据已经存在,return S3 URL 会为其创建一个任务,否则会在 SQS 中为其创建一个任务,生成数据并将其推送到 S3。根据您对不同艺术家的使用模式,您应该估计平均需要多少时间来收集数据,因此 return a URL 对 estimated_time_for_completetion(T) 的任务。

    客户端等待时间 T,然后向 URL returned 发出请求。在失败的情况下,最多可以说 3 次尝试获取此数据。事实上,S3 上已经存在的数据可以被认为是 T = 0.

    时的基本情况

    在这种情况下,您从客户端发出 2-4 个网络请求,但只有第一个请求到达您的服务器。只有在数据不存在并且客户端总是从 S3 拉入的情况下,您才将数据传输一次到 S3。

                               if exists on S3, return URL
    client --------> server --------------------------------> s3
                        |
                        |else SQS task
                        |---------------> generate content -------> save to S3 
                         return pre-computed url
    
    
               wait for time `T`
    client  -------------------------> s3
    


  1. 检查数据是否已经存在,并据此进行第二次网络调用

    这与您当前从服务器提供数据时所做的类似,以防它不存在。同样,我们在这里发出 2 个请求,但是,这次我们尝试从服务器同步提供数据,以防它不存在。

    因此,在第一次命中时,我们检查之前是否曾生成过内容,在这种情况下,我们会得到成功 URL 或错误消息。成功后,下一次点击进入 S3。

    如果 S3 上不存在数据,我们会发出一个新的请求(针对不同的 POST URL),在获取请求时,服务器计算数据,提供数据,同时添加将其推送到 S3 的异步任务。

                               if exists on S3, return URL
    client --------> server --------------------------------> s3
    
    client --------> server ---------> generate content -------> serve it
                                           |
                                           |---> add SQS task to push to S3