回到首页

输入关键词,多个关键词用空格分隔。

← 云计算课程 · 系列目录

云计算 第 13 课 缓存

约 8 分钟
本页目录

前面我们已经了解了如何进行 autoscaling 和自定义负载均衡器,这一课我们来接触另一个在 web 服务中非常重要的概念——缓存。


学习目标

  1. 了解在 web 服务中缓存所扮演的角色
  2. 比较影响缓存的两个方面:temporal locality 和 spatial locality
  3. 部署一个使用缓存的 web 服务
  4. 比较不同的缓存策略对性能的影响

先来看看数据格式,保存在 data center 中的数据格式是:

targetID(数字), first_name, last_name, encrypted data(文本)

而客户端(在这里就是 load generator)发给 data center 的数据格式是:

Target <targetID> first_name last_name’s encrypted conversations are: <conversations>

每个 data center 在同一时间大概能够缓存 10000 条记录,但是面对 load generator 仍然有些力不从心,我们的任务就是利用缓存来提高性能。

不少互联网应用都需要大量的资源,提高性能的方法很多,其中一个就是我们之前尝试过的 horizontal 和 vertical scaling。这种方式虽然比较方便,但是贵呀,能不能在已有资源限制的情况下,尽可能提高性能呢?两个比较常见的办法是『缓存(caching)』和『复制(replication)』。

复制、缓存和负载均衡可以在不对数据进行分割的情况下提高性能。很多工具(比如 Varnish, Squid, memcached)可以完成缓存这项任务,把最常用的资源保存下来。这样有两个好处:

  1. 系统不必重复运算,节约计算资源
  2. 可以快速向用户返回结果

另外一个比较常见的方法是『复制』,通常会与缓存结合使用。系统会保存多个全部数据集的副本,这样系统可以同时访问多个不同资源。使用『复制』方法的时候,需要注意以下几点:

  1. 需要确定一个分配用户请求的策略,可以是内容相关的(即特定请求会重定向至特定机器)或者是内容无关的
  2. 『复制』的实例本身需要实时监控,避免出现负载不均衡的问题
  3. 在进行内容写入的时候,还需要保证数据一致性(但是在这次的作业中不要求)

这周我们要处理的系统,后端有两个『复制』的数据库。除了可以应对大负载,某种程度上来说也提高了容错性(比如说数据库损坏)。任何时候,只有一个实例在运行,都是很危险的。具体的架构见下图:

图 A,从数据库中获取数据;图 B,从前端缓存中获取数据

说明以下上图的例子,前端访问数据库需要 1 ms(单向),从数据库中获取数据需要 10 ms,而直接访问前端获取一次数据需要 2 ms(单向),同一条记录,如果没有缓存,那么一共需要 16ms 来完成整个操作,而如果有缓存,则只需要 4 ms。

  • Temporal locality: 假如一条记录刚刚被用到,那么很可能之后也会被常用到
  • Spatial locality: 假如一条记录刚刚被用到,那么很可能它附近的几条记录也会被用到

另外一个需要注意的是,除了前端这个显式缓存,无论是数据库还是文件系统都会有自己的缓存系统,比方说 data center 会缓存最近的 10000 条记录,我们访问一条没有在前端缓存的记录时,如果它已经被 data center 缓存,那么整个可能寻找数据只需要 6 ms(而不是之前的 10 ms)。

注意事项

  • 用 Java 写,Azure 部分就有 100% 的分数,提交的代码中不要出现: Appid, appkey, storage account key 和 endpoint url
  • 最多只能在前端存储 1000 条记录
  • 做 AWS 的部分记得打上 Project : 2.3 的标签

系统镜像 Azure

  • Data Center, Standard_D1, https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p23dcv5-osDisk.dc552bc1-518d-451e-b856-c0419a6adcdb.vhd
  • Load Generator, Standard_D1, https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p23lgv4-osDisk.40d2443e-9f8c-41ce-9826-e0d7792a6c27.vhd
  • 前端, Standard_A0, https://cc15319619.blob.core.windows.net/system/Microsoft.Compute/Images/vhds/cc15619p23fe-osDisk.8d5f0df8-c94d-43e0-8a11-77ba440e0d8f.vhd

系统镜像 AWS

  • Data Center, m3.medium, ami-1b193371
  • Load Generator, m3.medium, ami-c00134aa
  • 前端, m3.medium, ami-6f2a1f05

缓存测试

研究 trace file 来确定缓存的策略。Data Center 各可以缓存 10000 条记录,并提供以下接口:

  • /target?targetID=X 返回结果并缓存
  • /range?start_range=X&end_range=Y 缓存一个范围内的结果(以 ; 分隔)并缓存

任务列表

  1. 启动一个 Standard_A0 虚拟机作为前端
  2. SSH 到前端,账户 ubuntu 密码 Cloud@123
  3. 文件夹 vertx 中有 MSB.java 和 run_vertx.sh
  4. 熟悉 MSB.java 中的代码
  5. 启动两个 Standard_D1 虚拟机作为 data center
  6. 在 MSB.java 文件中,把两个 data center 的 DNS 填写进去,格式为 xxx.eastus.cloudapp.azure.com
  7. 修改 retrieveDetails 函数,提高性能,现在代码已经是可以运行的,可以运行来体验下整个过程
  8. 可以在前端机器上存储 1000 条记录以提高性能。具体用什么数据结构和载入策略都可以自己决定。唯一的限制就是不能超出 1000 条的上限。
  9. 改动完成后可以用下面的命令启动 web 服务:./run_vertx.sh
  10. 确定代码没有问题后,可以开启一个 Standard_D1 虚拟机作为 load generator,在浏览器中访问其 DNS 就可以看到界面
  11. 提交密码和 andrew id
  12. 填写前端 DNS 地址以开始 Trace 1 测试,目标是 145 RPS
  13. 填写前端 DNS 地址以开始 Trace 2 测试,目标是 145 RPS
  14. 填写前端 DNS 地址以开始 MSB Trace 测试,目标是 145 RPS
  15. 测试的时候可以在浏览器里查看日志

提示

  • 研究清楚 web 服务的代码再开启 load generator 和 data center
  • 可以随意修改 MSB.java,唯一需要注意的是不能缓存超过 1000 条记录
  • 统计系统中的不同组件的延迟来找出哪里更加需要优化
  • 了解 temporal locality 和 spatial locality,思考如何可以在代码中利用这两个特性
  • 开始 MSB Trace Test 之前确保已经通过了前两个测试
  • 执行 run_vertx.sh 之后,可以使用 sudo cat nohup.out 来查看日志
  • 前两个测试可以通过观察请求中的 id 来进行策略设计,但 MSB Trace 测试需要更加复杂的缓存机制
  • 我们手头上有两台数据相同的 data center,各有不同的缓存,想想如何利用这个特点来提高效率(不同的请求发送给不同的主机,需要分类)
  • 了解同步和异步请求的概念,data center 提供 web 服务对数据库进行异步请求,看看这部分代码是怎么实现的
  • 这里只会对数据库进行读取,所以不需要考虑数据一致性的问题
  • 只需要提交 MSB Trace Test 的代码,把所有代码放到一个 zip 包里,用 load generator 提交
  • 每当 cache 有 miss 的时候,输出一下,找找规律

参考资料

AWS 部分

这一部分是选做的,基本要求一样,有以下需要注意的的:

  • 安全组中要打开 80 端口
  • AWS 的地址格式和 Azure 的稍有不同
  • 性能的要求是 142 RPS

项目日志

首先可以直接利用上个项目的代码,用指定的镜像开好四个机器,检查一下是否开机成功,都没问题之后,就可以 ssh 到我们的 Front End 机器:ssh ubuntu@yourdns.eastus.cloudapp.azure.com,然后把所有文件都复制下来方便本地修改:scp -r ubuntu@yourdns.eastus.cloudapp.azure.com:~/vertx/* ./,里面还是有不少文件的,不过我们这次只需要修改一个 MSB.java。

先来看看具体我们要做什么,观察 MSB.java,会发现这次处理请求的实现方式和之前都不一样,留意以下这一段代码

vertx.createHttpServer().requestHandler(new Handler<HttpServerRequest>() {
    public void handle(HttpServerRequest req) {
        String query_type = req.path();
        req.response().headers().set("Content-Type", "text/plain");
        if(query_type.equals("/target")) {
            String key = req.params().get("targetID");
            processRequest(key,req);
        } else if (query_type.equals("/range")) {
            String start = req.params().get("start_range");
            String end = req.params().get("end_range");
            processRequest(start, end, req);
            }
        }
    }).listen(80);

可以看到这里在 80 端口打开了一个监听器,每次遇到请求,就会调用其中的 handle(HttpServerRequest req) 函数,那么这个函数里做了什么事情呢?

  1. 根据不同的请求类型,执行不同的请求
  2. 具体的请求在 retrieveDetail 函数中完成

所以具体来说,要做好的是以下四个事情:

  1. 确定缓存所使用的数据结构
  2. 确定缓存替换记录的策略
  3. 确定需要缓存多少请求
  4. 确定两个 data center 的访问策略

基本上做好这四个事情,就算是完成任务了。修改完成之后可以把文件传回服务器 scp -r ./MSB.java ubuntu@yourdns.eastus.cloudapp.azure.com:~/vertx/

AWS 部分改动不多,改一下 data center 的地址就好,上传的代码为:scp -i demo.pem ./MSB.java ubuntu@dns.compute-1.amazonaws.com:~/,然后 ssh 过去运行 ssh -i demo.pem ubuntu@dns.compute-1.amazonaws.com

这次的作业比较简单,希望以后也能这么开心。

相关文章