【RPC框架之Finagle】

1、Finagle介绍

Finagle是Twitter基于Netty开发的支持容错的、协议无关的RPC框架,该框架支撑了Twitter的核心服务。

Twitter面向服务的架构是由一个庞大的Ruby on Rails应用转化而来的。为了适应这种架构的变化,需要有一个高性能的、支持容错的、协议无关且异步的RPC框架。在面向服务的架构之中,服务会将大多数的时间花费在等待上游服务的响应上,因此使用异步的库能够让服务并发地处理请求,从而充分发挥硬件的潜能。Finagle构建在Netty之上,并不是直接在原生NIO之上构建的,这是因为Netty已经解决了许多Twitter所遇到的问题并提供了干净整洁的API。

Twitter构建在多个开源协议之上,包括HTTP、Thrift、Memcached、MySQL以及Redis。因此,网络栈需要足够灵活,以保证能与这些协议进行交流并且还要具有足够的可扩展性以支持添加新的协议。Netty本身没有与任何特定的协议绑定,对其添加协议支持非常简单,只需创建对应的事件处理器(event handler)即可。这种可扩展性产生了众多社区驱动的协议实现,包括SPDY、PostrgreSQL、WebSockets、IRC以及AWS。

Netty的连接管理以及协议无关性为Finagle奠定了很好的基础,不过Twitter的有些需求是Netty没有原生支持的,因为这些需求都是“较高层次的”。比如,客户端需要连接到服务器集群并且要进行负载均衡。所有的服务均需要导出指标信息(如请求率、延迟等),这些指标为调试服务的行为提供了有价值的内部信息。在面向服务架构中,一个请求可能会经过数十个服务,所以如果没有跟踪框架的话,调试性能问题几乎是不可能的。为了解决这些问题,Twitter构建了Finagle。简而言之,Finagle依赖于Netty的IO多路复用技术(multiplexing),并在Netty面向连接的模型之上提供了面向事务(transaction-oriented)的框架。

2、Finagle的工作原理

Finagle强调模块化的理念,它会将独立的组件组合在一起。每个组件可以根据配置进行替换。比如,所有的跟踪器(tracer)都实现了相同的接口,这样的话,就可以创建跟踪器将追踪数据存储到本地文件、保持在内存中并暴露为读取端点或者将其写入到网络之中。

在Finagle栈的底部是Transport,它代表了对象的流,这种流可以异步地读取和写入。Transport实现为Netty的ChannelHandler,并插入到ChannelPipeline的最后。当Finagle识别到服务已经准备好读取数据时,Netty会从线路中读取数据并使其穿过ChannelPipeline,这些数据会被codec解析,然后发送到Finagle的Transport。从这里开始,Finagle将数据发送到自己的栈之中。

对于客户端的连接,Finagle维持了一个Transport的池,通过它来平衡负载。根据所提供的连接池语义,Finagle可以向Netty请求一个新的连接,也可以重用空闲的连接。当请求新的连接时,会基于客户端的codec创建一个Netty ChannelPipeline。一些额外的ChannelHandler会添加到ChannelPipeline之中,以完成统计(stats)、日志以及SSL的功能。如果所有的连接都处于忙碌的状态,那么请求将会按照所配置的排队策略进行排队等候。

在服务端,Netty通过所提供的ChannelPipelineFactory来管理codec、统计、超时以及日志等功能。在服务端ChannelPipeline中,最后一个ChannelHandler是Finagle桥(bridge)。这个桥会等待新进入的连接并为每个连接创建新的Transport。Transport在传递给服务器实现之前会包装一个新的channel。然后,会从ChannelPipeline之中读取信息,并发送到所实现的服务器实例中。



 

3、桥接Netty与Finagle

Finagle客户端使用ChannelConnector来桥接Finagle与Netty。ChannelConnector是一个函数,接受SocketAddress并返回Future Transport。当请求新的Netty连接时,Finagle使用ChannelConnector来请求一个新的Channel,并使用该Channel创建Transport。连接会异步建立,如果连接成功的话,会使用新建立的Transport来填充Future,如果无法建立连接的话,就会产生失败。Finagle客户端会基于这个Transport分发请求。

Finagle服务器会通过Listener绑定一个接口和端口。当新的连接创建时,Listener创建一个Transport并将其传入一个给定的函数。这样,Transport会传给Dispatcher,它会根据指定的策略将来自Transport的请求分发给Service。

4、对请求失败的管理

在扩展性的架构中,失败是常见的事情,硬件故障、网络阻塞以及网络连接失败都会导致问题的产生。对于支持高吞吐量和低延迟的库来说,如果它不能处理失败的话,那这样库是没有什么意义的。为了获取更好的失败管理功能,Finagle在吞吐量和延迟上做了一定的牺牲。

Finagle可以使用主机集群实现负载的平衡,客户端在本地会跟踪它所知道的每个主机。它是通过计数发送到某个主机上的未完成请求做到这一点的。这样的话,Finagle就能将新的请求发送给最低负载的主机,同时也就能获得最低的延迟。

如果发生了失败的请求,Finagle会关闭到失败主机的连接,并将其从负载均衡器中移除。在后台,Finagle会不断地尝试重新连接,如果Finagle能够重新建立连接的话,就会再次将其添加到负载均衡器之中。

Client Features

Connection Pooling

Load Balancing

Failure Detection

Failover/Retry

Distributed Tracing (à la Dapper)

Service Discovery (e.g., via Zookeeper)

Rich Statistics

Native OpenSSL Bindings

Server Features

Backpressure (to defend against abusive clients)

Service Registration (e.g., via Zookeeper)

Distributed Tracing

Native OpenSSL bindings

Supported Protocols

HTTP

HTTP streaming (Comet)

Thrift

Memcached/Kestrel

More to come!

猜你喜欢

转载自gaojingsong.iteye.com/blog/2341416