L3 Agent Responsiveness

https://blueprints.launchpad.net/neutron/+spec/l3-agent-responsiveness

作者:

Carl Baldwin <carl.baldwin@hp.com>

版权:

2014 Hewlett-Packard Development Company, L.P.

在代理重启时,L3 代理会循环遍历所有路由器,以确保它们与数据库同步。由于 rootwrap、sudo 和其他效率低下之处,此任务在负载较高的系统上可能需要一个多小时。此任务会锁定 RPC 处理,直到完成。从用户的角度来看,系统似乎对浮动 IP 和端口更改完全无响应。

问题描述

在代理重启时,L3 代理会立即启动一个名为 _sync_routers_task 的周期性任务。此任务获取一个信号量,该信号量会锁定 _rpc_loop,直到任务完成。这使得 L3 代理对通过 RPC 传入的新工作无响应。浮动 IP 需要等待变为活动或非活动状态,路由器网关无法插拔,并且无法操作子网端口。这给刚刚发出 API 调用以完成某事的用户的印象很差。

提议的变更

概述

此蓝图建议将 _sync_routers_task_rpc_loop 统一到一个处理循环中。这个单一的循环将优先处理 RPC 消息。换句话说,关于给定路由器的 RPC 消息会在队列中将该路由器提前,从而超过来自 _sync_routers_task 代码路径的所有路由器。

以这种方式优先级的理由是 _sync_routers_task 请求对路由器的维护更新。它的目的是捕捉一种不太可能的情况,即在代理关闭时对路由器进行了更改。RPC 消息通常代表通过 API 实时请求的系统更改。考虑到这一点,显然应该优先处理 RPC 消息,以改善用户体验。

公平地说,_sync_routers_task 在系统崩溃后重新启动时也有帮助。在这种情况下,这些更新不仅仅是维护。但是,在这种情况下,系统上的每个路由器都已经关闭。仍然明智的做法是优先响应用户请求。

每个更新都将携带一个时间戳,以便在同时存在许多具有相同优先级的更新时对其进行优先级排序。

并行性

当前的 L3 实现允许并行处理许多路由器。实际上,可以并行处理的路由器数量几乎没有限制,除了 1000 个绿色线程的限制。然而,实际上,并行处理 4-8 个路由器以上是不切实际的,因为存在足够多的争用点,以至于阻止线程之间的适当协作,以至于大多数线程都被饿死了。

此蓝图实现将创建一个 _process_routers_loop 来处理所有更新。此循环将使用固定大小的绿色线程池。该循环将持续生成工作线程,以确保最大数量的工作线程正在处理路由器或等待队列中的下一个更新。

如果需求足够,工作池的大小可以很容易地在后续蓝图中进行配置。但是,根据规模测试,初始大小将设置为 8。

ExclusiveRouterProcessor

将生成一个新的工作线程并立即调用 _process_router_update。此方法将立即查找队列中的下一个路由器更新。它将阻塞(友好的绿色线程风格阻塞),直到可用为止。

此时,有一些时序和协调问题需要考虑。ExclusiveRouterProcessor 类旨在处理这些问题。

首先,由于有多个工作线程和许多路由器,我们不希望有多个工作线程同时接触同一个路由器。为了避免这种情况,队列实现将返回一个 ExclusiveRouterProcessor 实例,该实例保证工作线程对路由器具有独占访问权限,即使在处理过程中有其他更新消息传入。此工作线程将被视为该路由器的 master,直到更新完成。

其次,有可能对路由器的新的更新会冒泡到优先级队列的前面,而路由器正在使用过时的信息进行处理。为了处理这种情况,获取此新更新的工作线程将尝试通过创建 ExclusiveRouterProcessor 的实例来获取对路由器的独占访问权限。此实例将意识到它不是 master 处理器,并且只会将更新附加到 master 实例将处理的更新列表中。

当 master 实例完成处理路由器时,它将检查其更新队列,以查看是否需要再次处理路由器。如果找到另一个更新,它将再次处理路由器,从 DB 获取新信息。它将循环,直到没有更多更新。这涵盖了用户在一段时间内积极对路由器进行更新的情况。路由器只需要连续多次处理,才能响应这些更新,直到用户完成为止。

重要的是要注意,新的更新必须冒泡到优先级队列的最前面,并且工作线程需要从队列中获取它,然后路由器处理器才会循环处理路由器并连续多次处理它。如果没有这个重要的区别,该算法将受到拒绝服务攻击,其中 8 个路由器可能会完全饿死系统上的所有其他路由器。

这个类中的复杂性主要在于保证对于任何给定的路由器只有一个 master 处理器。其余的围绕着下一步讨论的时序问题。

时序

每个更新都携带一个时间戳,该时间戳初始化为接收更新的时间。

ExclusiveRouterProcessor 类携带一个时间戳,该时间戳更新为在从数据库获取有关路由器的最新数据之前的时间。此时间戳会在使用这些数据处理路由器后记录。它将通过调用 fetched_and_processed 方法来记录。这非常重要,因为时间戳记录了最后用于完成系统上路由器更新的数据的年龄。这处理了数据获取时间和路由器最终更新之间的时间差。

_sync_routers_task 的情况下,相同的更新时间戳和数据年龄时间戳。但是,新的实现仍然会更新所有路由器。它们只是以低于 RPC 生成的更新的优先级进行更新。

仅当更新时间戳比最新的 router_data_timestamp 更新时,才会处理路由器的更新。

备选方案

加速 L3 代理是一项工作项目已经有一段时间了。在 Icehouse 时间范围内,在这方面已经取得了一些进展。例如,发现 sudo 存在一种效率低下,每次调用会增加 100 毫秒或更多的时间。这影响了 L3 代理及时配置路由器的能力。

在 Juno 时间范围内,我们将获得 rootwrap 的新守护程序模式,这将大大加快代理速度。

底线是,仅仅加快代理速度是不够的。在托管数百个路由器的代理上,_sync_routers_task 仍然会导致显著的延迟,这会影响最终用户体验。

数据模型影响

REST API 影响

安全影响

通知影响

其他最终用户影响

性能影响

改进了在代理重启后对通过 API 进行的 L3 更改的响应速度。

其他部署者影响

此更改将使使用 L3 代理的大规模云部署的部署者可以更轻松地部署。他们将能够部署代码库的更新,重启 L3 代理,而不必担心它对系统整体响应速度的影响。

开发人员影响

实现

负责人

主要负责人

carl-baldwin

其他贡献者

工作项

https://review.openstack.org/#/c/78819

依赖项

测试

不需要新的 gate 测试,因为这不会改变功能。该实现将完全经过单元测试,包括新的测试,以涵盖优先级队列和路由器处理器的功能。

文档影响

参考资料

https://review.openstack.org/#/c/78819