5. 路由模块/前端
前端服务器提供了HTTP端口进行访问。它们也负责设置正确的CDN头,安全验证(反病毒,异常内容检测)和序列化对象发送给路由库和客户端。
路由库包括请求管理逻辑的核心并且镶嵌在前端中。同样地,他也可以作为客户端独立库来提高性能。
5.1 路由模块的职责
- 请求管理:端到端请求的生命周期由路由模块管理。路由模块处理put、get和delete请求。对于每个请求,路由模块会记录每个备份成功的次数还有失败的次数来进行决议,维护数据块的状态,生成object ID,并回调响应成功或者失败。
- 数据块: 大对象会被分解成数据块,每一个数据块会被路由到不同的分片上。每个数据块都有唯一标识的ID。路由模块生成包含一组数据块信息和这些数据块访问顺序的元数据对象。这个元数据对象被保存为独立的BLOB,id为元数据对象的ID。在读取时,这个元数据BLOB被读取,数据块被恢复出来并发送给客户端。路由模块流式处理这些数据块请求,保证下一个数据块总是就绪状态。路由模块也做内存管理来保证内存中的数据块个数是一定的而且可配置。
- 失败检测:这是保证路由模块高可用的另一核心功能。失败检测用来主动发现挂掉的或者状态不好的资源。这些资源包括:机器,磁盘或者分片。机器也许会挂掉,磁盘也许会停止工作并且分区可能会不可用。路由模块标记资源不可用来确保之后的请求不会用到他们。在PUT时,只有运行正常的分片会被选择。在GET时,挂掉的资源会被忽略掉,请求只会被发送到健康的。失败检测器负载真正的请求,并且利用发散法去鉴别挂掉的资源,并主动监测资源时候回归到健康状态。
- **仲裁:**Ambry实现了一个多主读写的策略。这减少了主从系统明显存在的额外访问,并确保了更高的可用性。请求被发送至M个备份并等待至少N个成功响应。路由模块给这些本地机房的备份排好发送请求的顺序。如果仲裁不能在本地完成,则代理请求至远程。在实际应用中,PUT和DELETE请求需要更强的仲裁来保证强持久性。读请求通常发生在一个备份上,但是在失败时会发送多个请求,来减少失败的可能性
- 变化捕捉:路由模块也会在每个成功的PUT请求还有DELETE请求时生成变化捕捉。在变化捕捉中的信息包括blob的ID和这个blob的元数据。这个消息可以被下游系统消费来做一些产品。例如搜索服务更新索引,相关系统重新运行算法,或者是个流入系统来展示所有的新媒体内容。
5.2 路由模块操作
我们来看下一个简单的PUT和GET操作。实际上这些操作比展示的更复杂,但是这里为了方便大家阅读,做了一些简化:
- PUT操作:客户端将序列化对象流与一些元信息发送到前端。当流到达时,前端将对象分块选择一个可用的分片,生成blob id并把请求发送给W个备份。在返回blob id给客户端之前,前端等待至少Q(<=W)个成功响应。如果仲裁失败,前端报告错误。我们将这个机制优化成选择另外一个分片。这使我们在PUT请求时达到了更高的可用性并能承受更多的下游失败。
- GET操作:客户端通过将blob ID发送给前端来请求一个对象的blob。前端通过这个ID来决定这个对象在那个分片上,并从对应的数据节点上抓取相关的数据块。对于每一个数据块,前端会发送R个请求到每一个备份上,在将数据返回给客户端之前,前端等待至少Q(<=R)个成功。在实际应用中,只有在防止失败时,这个仲裁机制才会生效。然而,这无疑增加了数据节点的负载。我们希望未来优化中能实现只在某一固定超时之后在发送剩下的请求。这能确保我们在大部分情况下能减少数据节点的负载。