# Sify.com 体系结构-每秒 3900 个请求的门户

> 原文： [http://highscalability.com/blog/2010/5/10/sifycom-architecture-a-portal-at-3900-requests-per-second.html](http://highscalability.com/blog/2010/5/10/sifycom-architecture-a-portal-at-3900-requests-per-second.html)

![](img/cfd4275d142a1fcf3dc491330469eee9.png)

Sify.com 是印度领先的门户网站之一。 Samachar.com 由同一家公司拥有，是印度顶级的内容汇总网站之一，主要针对来自世界各地的非居民印度人。 Sify 的建筑师 Ramki Subramanian 非常慷慨地描述了这两个站点的通用后端。 他们的架构最值得注意的方面之一是 Sify 不使用传统数据库。 他们查询 Solr，然后从分布式文件系统中检索记录。 多年以来，许多人争相使用数据库之上的文件系统。 文件系统可以用于键值查找，但不适用于查询，使用 Solr 是解决该问题的好方法。 他们系统的另一个有趣的方面是使用 Drools 进行智能缓存无效化。 随着我们在多个专业服务中复制越来越多的数据，如何使它们[同步](http://www.youtube.com/results?search_query=in+sync)成为一个难题。 规则引擎是一种聪明的方法。

## **平台/工具**

*   的 Linux
*   [浅色](http://www.lighttpd.net/)
*   PHP5
*   记忆快取
*   阿帕奇·索尔（Apache Solr）
*   Apache ActiveMQ /骆驼
*   GFS（群集文件系统）
*   德语
*   雷迪斯
*   带有 ActiveMQ 的子。
*   漆
*   流口水

## 统计资料

*   每月约有 1.5 亿次网页浏览。
*   服务 3900 请求/秒。
*   后端在托管约 30 个 VM 的 4 个刀片上运行。

## 建筑

*   该系统已完全虚拟化。 我们还使用了大多数 VM 功能，例如当一个刀片发生故障或需要重新分配负载时，我们在刀片之间移动 VM。 我们已经对虚拟机进行了模板化，因此我们可以在不到 20 分钟的时间内配置系统。 它目前是手动的，但是在系统的下一版本中，我们计划自动化整个供应，调试，退役，在 VM 周围移动以及自动缩放。
*   没有数据库
*   100％无状态
*   RESTful 接口支持：XML，JSON，JS，RSS / Atom
*   写入和读取具有不同的路径。
    *   通过 ActiveMQ / Camel 将写入排队，转换并路由到其他 HTTP 服务。 它用作 ESB（企业服务总线）。
    *   像搜索一样的读取操作是由 Web 服务器直接从 PHP 处理的。
*   Solr 用作索引/搜索引擎。 如果有人要求提供密钥的文件，则会直接将其送出存储空间。 如果有人说“给我所有在 author = Todd 处的文件，”它将打到 Solr，然后存储。 使用 Apache Solr 作为我们的搜索引擎来执行查询，并且我们有相同的分布式设置。
*   所有文件都存储在群集文件系统（GFS）中。 查询打到 Solr，它返回我们想要的数据。 如果我们需要完整的数据，则在从搜索中获取 ID 后，我们将访问存储。 这种方法使系统完全可以水平扩展，并且对数据库的依赖性为零。 升级到最新版本后，对我们来说效果很好。 我们仅运行 2 个节点进行存储，如果需要，我们可以再添加几个节点。
*   轻巧的前端 GFS。 Lighty 对于提供静态文件确实非常好。 对于我们拥有的文件类型（主要是小的 XML 和图像），每秒可以随意接收 8000 多个请求。
*   所有最新的 NoSQL 数据库（如 CouchDB，MongoDB，Cassandra 等）都将替代我们的存储层。 在搜索功能上，它们都不接近 Solr / Lucene。 就查询而言，MongoDB 是最好的，但是``包含''之类的搜索需要使用正则表达式来完成，这对 500 万个文档来说是一场灾难！ 我们相信，目前，基于分布式文件系统的方法比许多用于存储的 NoSQL 数据库系统更具可伸缩性。

## 未来

*   用于事件分析（用户点击，实时图表和趋势）的 CouchDB 或 Hadoop 或 Cassandra。
*   使用 Drools 的智能缓存失效。 数据将通过队列推送，而 Drools 引擎将确定哪些 URL 需要无效。 它将在我们的缓存引擎或 Akamai 中清除它们。 方法是这样的。 查询（URL）到达我们的后端后，我们将记录该查询。 然后，记录的查询将被解析并推送到 Drools 系统中。 如果该输入尚不存在，则系统会将其输入并动态创建规则到系统中。 那是 A 部分。然后，我们的 Content Ingestion 系统将继续将进入 Drools 队列的所有内容推送。 数据输入后，我们将针对内容触发所有规则。 对于每个匹配的规则，生成 URL，然后我们将针对这些 URL 向缓存服务器（Akamai 或 Varnish）发出删除请求。 多数民众赞成在 B 部分。B 部分并不像上面提到的那么简单。 会有很多不同的情况。 例如，我们在查询中支持“ NOW”，大于，小于，NOT 等，这确实会让我们头疼。
    *   我们这样做的主要原因主要有两个，高速缓存命中率极高，并且几乎可以立即更新最终用户。 请记住，过去从未经历过的两个原因！
    *   我认为它将很好并且可以扩展。 Drools 确实擅长解决此类问题。 同样在分析中，我们发现查询在许多天内都是恒定的。 例如，我们每天有将近 40,000 个不同的查询，并且每天都会以几乎相同的模式重复。 对于该查询，只有数据会更改。 因此，我们可以设置多个实例并仅在不同系统中复制规则，这样我们也可以水平扩展它。
*   同步读取，但是更少的层，更少的 PHP 干预和套接字连接。
*   使用 Queue / ESB（Mule）进行分布式（写入不同的分片）和异步写入。
*   使用 Varnish 或 Akamai 进行大量缓存。
*   杀死 gron 并保持更接近实时的守护进程。
*   使用 Gearman 进行并行和后台处理，以及用于自动缩放处理的自动附加处理功能。
*   使用 Kaazing 或 eJabberd 向最终用户和内部系统实时分发内容。
*   Redis 用于缓存内容摘要以确定重复项。
*   我们正在寻求使整个事情更易于管理，并从 app-admin 内部打开 VM 和进程。 我们研究了 Apache Zookeeper，研究了 VMWare 和 Xen 提供的 RESTful API，并与我们的系统进行了集成。 这将使我们能够进行自动缩放。
*   我们拥有的最大优势是数据中心的带宽不受限制，因为我们自己就是 ISP。 我正在研究在系统中利用该优势的方法，并了解如何构建可以并行快速处理大量内容的集群。

## 得到教训

*   ActiveMQ 多次被证明是灾难性的！ 套接字处理非常差。 从重启开始，我们通常会在不到 5 分钟的时间内达到 TCP 套接字限制。 尽管它声称已在 5.0 和 5.2 中进行了修复，但对我们而言并不起作用。 我们以多种方式尝试使它的寿命更长，至少一天。 我们通过部署带有新版本的旧库来解决问题，并使它的使用时间更长。 毕竟，我们部署了两个 MQ（消息队列），以确保至少对内容进行编辑更新可以正常进行。
    *   后来我们发现问题不仅在于此，而且使用主题也是一个问题。 仅对四个订阅者使用 Topic 会使 MQ 在数小时内挂起。 在大量脱发后，我们取消了整个基于主题的方法，并将它们全部排入队列。 数据进入主队列后，我们将数据推入四个不同的队列。 问题已解决。 当然，在 15 天左右的时间内，它将抛出一些异常或 OOME（内存不足错误），并迫使我们重新启动。 我们只是与它共存。 在下一个版本中，我们将使用 Mule 来处理所有这些问题，并且也将集群同时进行。 我们还试图找到一种方法来按消息顺序摆脱依赖关系，这将使分发变得更容易。
*   索尔
    *   重新启动。 我们必须非常频繁地重新启动它。 尚不十分清楚原因，但是因为它具有冗余性，所以我们比 MQ 更好。 通过执行查询，我们已达到自动重启的程度，如果没有响应或超时，我们将重启 Solr。
    *   复杂查询。 对于复杂的查询，查询响应时间确实很差。 我们有大约 500 万个文档，并且很多查询的确在不到一秒钟的时间内返回，但是当我们有一个带有几个“ NOT”以及许多字段和条件的查询时，它需要 100 秒钟以上的时间。 我们通过将查询拆分为更简单的查询并将结果合并到 PHP 空间中来解决此问题。
    *   即时的。 我们遇到的另一个严重问题是 Solr 不能实时反映所做的更改。 大约需要 4 分钟到 10 分钟！ 考虑到我们所处的行业和竞争，迟到的新闻 10 分钟使我们变得无关紧要。 看过 Zoie-Solr 插件，但我们的 ID 是字母数字，而 Zoie 不支持。 我们正在寻找自己在 Zoie 中修复的问题。
*   GFS 锁定问题。 对于我们来说，这曾经是一个非常严重的问题。 GFS 将锁定整个群集，这将使我们的存储完全不可访问。 GFS 4.0 出现问题，我们升级到 5.0，从那时起似乎还不错。
*   Lighty 和 PHP 不太融洽。 性能方面都不错，但是 Apache / PHP 更稳定。 由于 PHP_FCGI 进程挂起，Lighty 有时会胡思乱想，CPU 使用率达到 100％。

我非常感谢 Ramki 花时间写关于他们的系统如何工作的信息。 希望您能从他们的经验中学到一些有用的东西，这些对您自己的冒险有帮助。 如果您想共享您的优秀系统的架构，请同时向其付费和向后付费，请 [与联系我](../../contact/)，我们将开始。

相当有趣的信息！ 谢谢分享！ 每秒 3900 个请求非常棒。

目标响应时间是多少？

拉姆基

Lighty 曾经有内存泄漏。 Nginx 或 Cherokee 可能值得一看。 将 MogileFS 视为 GFS 的替代品-GFS 更像是 POSIX 文件系统的替代品，但是 MogileFS 可能“足够”，并且应该具有更大的可扩展性。 Solr －也许您需要分片搜索引擎，但这可能比它值得的工作更多。 ;-)使用 ActiveMQ，您是否考虑过将一些较轻的排队需求转移到 Redis？ 它具有可用于某些类型排队的原子构造，并且几乎可以肯定会更快。

感谢如此出色的文章！

杰米

精彩的帖子！ 很高兴看到有人实际构建事物而不是遵循流行语。

这些“ NoSQL”文档存储为您直接的分布式文件系统添加的功能正是使搜索与数据保持同步的能力。 有趣的是，与“愚蠢”的文档存储（文件系统）相比，您实际上为此功能付出了高昂的代价。 在我看来，保持这种状态的关键是在 Solr 搜索和数据之间出现延迟。 您愿意允许多少时间？

考虑到这一点，对我来说，将 CouchDB 用作分布式文件系统似乎是个好主意。 我不敢相信它会比您使用的任何工具都要慢。 还有...继续使用 Solr！ 仅在需要同步搜索时才退回到 CouchDB“视图”。

这似乎与“ NoSQL”的智慧几乎相反，但是从您的经验来看，这似乎是一个很好的实用结论。

您是在 4 刀片系统上直接处理 3900 Request / second，还是还包括 Akamai 流量？
您如何处理负载平衡？

> “后端在托管约 30 个 VM 的 4 个刀片上运行。”

what kind of 'front-end' infrastructure do you have?
What kind of VM are you running? VMWare? Xen? KVM?

听起来好像他们正在将 HP Blade Matrix 与 VMware 一起使用。

“每月有 1.5 亿页面浏览量”
“每秒可处理 3900 请求”。

算一下：3900 * 60 * 60 =每小时请求 14.000.000
每天 8 小时：每天 14.040.000 * 8 = 112.000.000
因此，其中一个 上面的值

Raghuraman：目标响应时间是多少？

拉姆基：我们不需要在后端系统中执行超过数千个 rp，因为前端系统的许多部分速度都较慢。 毕竟，系统中的薄弱环节定义了最终的要求/秒！ 如果我们可以使前端数量相同，那就太好了！

杰米：

Lighty 曾经有内存泄漏。 Nginx 或 Cherokee 可能值得一看。

Ramki：到目前为止，Lighty 对我们的表现一直很好，尽管在某些情况下，lighty 会让我们头疼。 Nginx 是一个不错的选择，我们可以尝试一下。

杰米（Jamie）：将 MogileFS 视为 GFS 的替代品-GFS 更像是 POSIX 文件系统的替代品，但 MogileFS 可能“足够”并且应该具有更大的扩展性。

Ramki：该系统的第一个版本在 MogileFS 中运行，并很快成为我们的大问题。 给我，在数据库中存储文件的元信息是一场灾难。 在短短几周内，数据库记录就接近 150 万。 查询变得非常缓慢...但是我喜欢基于 WebDAV 的方法的想法，因此我们尝试了不使用 MogileFS，但是我们的 LB 当时不支持 HTTP 1.1，因此我们取消了整个想法并将其移至文件系统。

杰米（Jamie）：索尔（Solr）-也许您需要分片搜索引擎，但这可能比它的价值还多。 ;-)

拉姆基：是的，我们已经记了这个。

杰米：使用 ActiveMQ，您是否考虑过将一些较轻的排队需求转移到 Redis？ 它具有可用于某些类型排队的原子构造，并且几乎可以肯定会更快。

Ramki：我们对 ActiveMQ 有点太深了。 它主要执行路由，服务抽象并为我们保证交付。 对于那些，我不会押注 Redis。 希望我回答了你的问题。

Tal：这些“ NoSQL”文档存储为您直接的分布式文件系统添加的功能正是使搜索与数据保持同步的能力。 有趣的是，与“愚蠢”的文档存储（文件系统）相比，您实际上为此功能付出了高昂的代价。 在我看来，保持这种状态的关键是在 Solr 搜索和数据之间出现延迟。 您愿意允许多少时间？

Ramki：如文章中所述，DFS 用作键值存储。 并且我们已经设置了 MQ 先写入存储然后再写入 Solr。虽然将其推入 Q 使其异步，但数据的移动在 Q 内部是顺序的。这样一来，您将无法获得返回任何不存在的内容的搜索 在存储中。 同样，假设存储节点位于本地附近，则数据会立即反映出来。 希望我回答了你的问题。

Tal：考虑到这一点，对我来说，将 CouchDB 用作分布式文件系统似乎是个好主意。 我不敢相信它会比您使用的任何工具都要慢。 还有...继续使用 Solr！ 仅在需要同步搜索时才退回到 CouchDB“视图”。

Ramki：我们正在尝试使用 CouchDB 进行分析。 在开发环境中，我们的速度仅为每秒 1000 请求。 有了存储和轻松的设置，我们便可以轻松完成 8000+ reqs / s。 当然，1000 res / s 也是非常好的数字。

Tal：这似乎与“ NoSQL”的智慧几乎相反，但是从您的经验来看，这似乎是一个很好的实用结论。

Ramki：不确定是否与 NoSQL 相反。 原则上我们离它更近。 例如：没有“ sql” :)，几乎没有架构。 我说这几乎是因为 Solr 迫使我们拥有一些架构，分布式和键值之类的存储。

Maxim：您是在 4 刀片系统上直接处理 3900 Request / second，还是还包括 Akamai 流量？

Ramki：这里没有 Akamai。 它是我们所有的后端，不在 akamai 上。 一旦我们有了用于 URL 无效的缓存引擎和规则方法，我们将远远超过 3900 r / s。 我们的开发测试是 8500+ r / s 的清漆。 但是请记住，考虑到延迟，这些数字可能并不完全是我们将从外界得到的数字。

Maxim：您如何处理负载平衡？

拉姆基：这现在可以通过一种弯腰/麻木的方式来解决：)。 我们的资源有限，但是我们需要“无 SPOF”，因此我们在同一个 LB 内针对不同层有点循环。 考虑到 H / W LB 的成本，我们可能不得不采用这种方法。 我们正在寻找 S / W LB 作为替代方案，但我们需要使用刀片来部署它们！ :)

塔尔：您拥有什么样的“前端”基础架构？

Ramki：我们也在这里迁移到基于 VM 的环境。 但是再过几个月，我们将完全使用 VM，并在我们现在构建的系统之上运行。 旧系统将被淘汰。

希望我已经回答了问题。

请确保提出建议。 我希望得到社区的反馈。

-拉姆基

嗨，
我研究的一件事是每秒 3900 req。

您的每日网页价值为 1544943（http://www.websiteoutlook.com/www.sify.com）。

因此，假设每位用户 5 次浏览量将带来每天 30 万用户或每小时 12500 位用户或每分钟 208 位用户。

或大约每秒 3 个用户访问服务器。

所以我的问题是 3 个用户每秒可以生成 3900 个请求吗？

RB & GK：如前所述，整篇文章都是关于后端而不是前端。 正如您在我上面的评论中所看到的那样，“如果我们能够使前端数量大约相同，那将是非常棒的事情！”

后端不仅会受到前端的攻击。 后端有许多直接 API 调用。

因此，以上计算并不能完全转换为后端匹配。

也就是说，我们已经在开发前端，希望我们能达到接近后端的数字。

如果你们在同一方面确实有一些投入，那就太好了。

-ramki

工具的传播非常有趣。 您对文件系统复制器有何建议？ 你是怎样做的 ？
我们在一个城市中拥有 Windows SAN 存储。 我们想将添加和删除的文件实时复制到另一个城市的另一个 Windows 框中。 仅当完全写入文件时，才应复制文件。

杰米：“ Lighty 曾经有内存泄漏。”

过去……过去……一段时间以前……这有什么意义？
Apache 过去有一些问题，nginx 过去有一些问题。
软件并不完美，并且会通过更新得到修复。
我运行了一堆 lightys，它们处理几乎相同数量的静态文件请求（8000 req / s）而不会费力。
以及 PHP 的一些 Hundret re / s 也没有问题。

很多人将内存泄漏误认为是 Lighty 缓冲来自后端（例如 PHP）的请求。 因此，如果您通过 Lighty 从 PHP 发送 10MB 数据，它将使用 10MB。 并且它将重新使用此内存以供以后的请求使用。
为什么这样做？ 好吧，它有助于加快请求的速度，因为您只有数量有限的 PHP 后端，否则这些后端将被速度较慢的客户端占用。 它可以更快地释放 PHP“插槽”。
我会在任何时候减少使用 RAM 的情况。
要记住的唯一事情是不要通过 lighty 通过 PHP 发送大文件，这反而是一个坏主意，因为它还有其他一些原因（为此使用 X-sendfile 标头或直接提供它们）。

因此，为了做出决定，而不是走“呃，但我听说有人过去有问题..”，而不得不*立即*并针对您的用例来研究它的工作方式。

我想讲的教训是：使用现在（以及将来当然）可以最好地完成任务的工具。

我以前曾经在 PHP 中看到过 100％CPU 的情况，这很奇怪，在某些情况下更新 PHP 很有帮助。 不幸的是，PHP 的 FastCGI 接口不如 mod_php 稳定。 但是它工作得很好。

Ramki：感谢您分享您的架构和经验教训，我发现无数据库方法特别有趣和令人耳目一新。 对 NoSQL 想法的深入了解可能不是更好的选择。 荣誉

没有，

我不确定 websiteoutlook 的统计信息，但对于我知道其审计数的几个网站，它们显得有些低（10 倍）。

无论如何，每月有 1.5 亿次页面浏览量（是网站外观数字的 3 倍），平均每秒 57 次页面浏览量。 3900 次/秒是每页浏览 68 次（您的 3 个用户（真正是 3.57 个用户）x 5 页/访问 x 不足 3 的因素）。 头版页面有 80 个元素，即使缓存率很高，考虑到高峰期和一个前端请求也会导致多个后端请求，因此乘数似乎并不是很大的乘数。

广泛的数字对我来说似乎很合理。

关于进行综合浏览和请求计算的人员：

将网页浏览量视为用户的完整呈现页面是很常见的，这反过来可能转换为针对后端的 1 到 50 个请求，具体取决于呈现页面所需的后端信息源的数量。

您无法对它们进行计算，因为您将对苹果和梨进行计算。
每月观看次数总计超过 30 天，而 3900 req / s 则是特定时刻的峰值

根据视图的数量和使用情况的一些假设，您可以计算出大约并发用户的平均数，该数量永远不会接近峰值时的并发用户数。

您正在运行哪个 GFS 版本的 GFS1 或 GFS2？ 您将在文件系统中存储多少数据？
我们曾经将许多数据存储在 GFS（1）中，但是由于可伸缩性问题而远离了它。

主要问题是锁定，无法扩展文件系统而没有停机时间以及对复制/冗余的不良/不存在的支持。 从那时起，我们的数据量已经增长到了惊人的水平。 1 PB。
我也很好奇您用于在以下位置存储 GFS 数据的基本存储硬件：iscsi，光纤，AoE？

拉蒙

Mohan Radhakrishnan：您对文件系统复制器有何建议？ 你是怎样做的 ？ 我们在一个城市中拥有 Windows SAN 存储。 我们想将添加和删除的文件实时复制到另一个城市的另一个 Windows 框中。 仅当完全写入文件时，才应复制文件。

Ramki：我们已经尝试解决这个问题已有相当长的时间了。 到目前为止，我们仅在一个数据中心上运行，并且 GFS 集群安装在局域网中的 2-3 个节点上，因此其他节点可以立即看到每次写入。 鉴于此，我们没有复制问题。

但是，我们已经在寻找在大约 800 英里之外的印度两个城市中部署应用程序的选项，我们希望使用主动-主动设置。 根据上下文，我们要求存储提供商在两个城市之间进行实时复制。 作为 ISP，我们的延迟相对较小（我认为），大约 40 毫秒。 即使有这种延迟，任何存储提供商都无法做到近乎实时。 有一家公司说我们可以做到，并要求他们证明这一点。 他们部署了箱子，这是灾难性的失败！ 当然，这都是异步复制，同步复制是同时关闭两个站点的最佳方法！ 玩笑！ :)同步锁定源文件系统，同步到另一个站点，其他站点锁定文件系统，写入，确认到源，源站点释放锁定。 在这段时间内，编写文件的客户端正在等待……您猜对了。

总结：
-如果您的延迟超过 5 毫秒且实时，则任何存储提供商都无法做到这一点。 即使延迟对您来说较小，也请他们先进行证明。
-构建您自己的复制系统。 保持异步。

我们现在正在考虑做的是：

我们已经将内容推送到 MQ。 现在，内容也将被推送到其他城市的另一个队列。 这样，几乎可以立即在两个位置复制数据。 然后我们将接受可能会有的轻微延迟。 我认为延迟将少于 5 秒，这比存储提供商建议的 10-15 分钟复制更好。 无论如何，我们必须自己进行测试。 我们拥有的一大优势是，甚至文件的删除也是 XML 请求，因此也要经过 Q。

对于较大的二进制文件（视频&图像），我们采用略有不同的方法。 所有这些都写入临时目录，并且守护程序将文件移动到适当的目录。 我认为有 2 个城市时，这不会给我们带来很多头痛。 除了基于 ESB / MTOM 的方法外，别无其他想法，并通过队列推送元数据。

Ramon：您正在运行哪个版本的 GFS 或 GFS2？ 您将在文件系统中存储多少数据？
我们曾经将许多数据存储在 GFS（1）中，但是由于可伸缩性问题而远离了它。

主要问题是锁定，无法扩展文件系统而没有停机时间以及对复制/冗余的不良/不存在的支持。 从那时起，我们的数据量已经增长到了惊人的水平。 1 PB。

Ramki：我们在 GFS1 上遇到了同样的问题，我们现在在 GFS2 上。 但是，当我们必须在 VM 上运行节点时，仍然存在扩展问题。 支持的虚拟机数量不超过 2 个，添加集群的第 3 个节点有时会挂起。 也许这也是 GFS1 的问题，我们担心被吊死。 您对此有一些了解吗？ 当前数据大小约为 1.5 Tera。 我们尚未将旧内容迁移到新系统。

拉蒙：我也想知道您用来在 GFS 数据上存储 iscsi，光纤，AoE 的底层存储硬件吗？

Ramki：iSCSI

希望答案。

-ramki

您还将向用户提供电子邮件。 您是否还保留没有数据库的身份验证记录？

另外，你们使用哪个邮件服务器？

嗨 Ramki，

想知道你们是否看过 AWS（http://aws.amazon.com）？ 在它们上运行会简化你们正在做的许多 VM 管理工作吗？

它们还提供了负载平衡和自动扩展解决方案（http://aws.amazon.com/autoscaling/）和队列服务（http://aws.amazon.com/sqs/）。

嗨，杜沙尔，

我们查看了 AWS 的其他项目。 我们面临的最大问题是延迟。 我们不能忍受这种延迟。

除此之外：
-我们有自己的管道
-我们在各个城市都有自己的数据中心。
-我们已经在下文中介绍了许多内容。

因此，在我们的案例中，这实际上没有任何意义。

-ramki