文档章节

Twitter的RPC框架Finagle简介

tqyin
 tqyin
发布于 2016/09/30 11:04
字数 2867
阅读 75
收藏 2
点赞 0
评论 0

        Finagle是Twitter基于Netty开发的支持容错的、协议无关的RPC框架,该框架支撑了Twitter的核心服务。来自Twitter的软件工程师Jeff Smick撰文详细描述了该框架的工作原理和使用方式。
        在Jeff Smick的博客文章中,介绍了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)的框架。

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之中读取信息,并发送到所实现的服务器实例中。

Twitter的RPC框架Finagle简介

  1. Finagle客户端位于Finagle Transport之上,这个Transport为用户抽象了Netty;
  2. Netty ChannelPipeline包含了所有的ChannelHandler实现,这些实现完成实际的工作;
  3. 对于每一个连接都会创建Finagle服务器,并且会为其提供一个Transport来进行读取和写入;
  4. ChannelHandler实现了协议的编码/解码逻辑、连接级别的统计以及SSL处理。

桥接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。

Finagle的抽象

      Finagle的核心概念是一个简单的函数(在这里函数式编程很关键),这个函数会从Request生成包含Response的Future:

type Service[Req, Rep] = Req => Future[Rep]

      Future是一个容器,用来保存异步操作的结果,这样的操作包括网络RPC、超时或磁盘的I/O操作。Future要么是空——此时尚未有可用的结果,要么成功——生成者已经完成操作并将结果填充到了Future之中,要么失败——生产者发生了失败,Future中包含了结果异常。这种简单性能够促成很强大的结构。在客户端和服务器端,Service代表着相同的API。服务器端实现Service接口,这个服务器可以用来进行具体的测试,Finagle也可以将其在某个网络接口上导出。客户端可以得到Service的实现,这个实现可以是虚拟的,也可以是远程服务器的具体实现。
       比如说,我们可以通过实现Service创建一个简单的HTTP Server,它接受HttpReq并返回代表最终响应的Future[HttpRep]:

val s: Service[HttpReq, HttpRep] = new Service[HttpReq, HttpRep] {
def apply(req: HttpReq): Future[HttpRep] =
    Future.value(HttpRep(Status.OK, req.body))
}
Http.serve(":80", s)

这个样例在所有接口的80端口上暴露该服务器,并且通过twitter.com的80端口进行使用。但是,我们也可以选择不暴露服务器而是直接使用它:

server(HttpReq("/")) map { rep => transformResponse(rep) }

在这里,客户端代码的行为方式是一样的,但是并不需要网络连接,这就使得客户端和服务器的测试变得很简单直接。
      客户端和服务器提供的都是应用特定的功能,但通常也会需要一些与应用本身无关的功能,举例来说认证、超时、统计等等。Filter为实现应用无关的特性提供了抽象。

Filter接受一个请求以及要进行组合的Service:

type Filter[Req, Rep] = (Req, Service[Req, Rep]) => Future[Rep]

在应用到Service之前,Filter可以形成链:

recordHandletime andThen
traceRequest andThen
collectJvmStats
andThen myService

这样的话,就能够很容易地进行逻辑抽象和关注点分离。Finagle在内部大量使用了Filter,Filter有助于促进模块化和可重用性。

Filter还可以修改请求和响应的数据及类型。下图展现了一个请求穿过过滤器链到达Service以及响应反向穿出的过程:

Twitter的RPC框架Finagle简介

对请求失败的管理

       在扩展性的架构中,失败是常见的事情,硬件故障、网络阻塞以及网络连接失败都会导致问题的产生。对于支持高吞吐量和低延迟的库来说,如果它不能处理失败的话,那这样库是没有什么意义的。为了获取更好的失败管理功能,Finagle在吞吐量和延迟上做了一定的牺牲。
      Finagle可以使用主机集群实现负载的平衡,客户端在本地会跟踪它所知道的每个主机。它是通过计数发送到某个主机上的未完成请求做到这一点的。这样的话,Finagle就能将新的请求发送给最低负载的主机,同时也就能获得最低的延迟。
     如果发生了失败的请求,Finagle会关闭到失败主机的连接,并将其从负载均衡器中移除。在后台,Finagle会不断地尝试重新连接,如果Finagle能够重新建立连接的话,就会再次将其添加到负载均衡器之中。

服务的组合

        Finagle将服务作为函数的理念能够编写出简单且具有表述性的代码。例如,某个用户对其时间线(timeline)的请求会涉及到多个服务,核心包括认证服务、时间线服务以及Tweet服务。它们之间的关系可以很简洁地进行表述:

val timelineSvc = Thrift.newIface[TimelineService](...) // #1
val tweetSvc = Thrift.newIface[TweetService](...)
val authSvc = Thrift.newIface[AuthService](...)
val authFilter = Filter.mk[Req, AuthReq, Res, Res] { (req, svc) => // #2
           authSvc.authenticate(req) flatMap svc(_)
}
val apiService = Service.mk[AuthReq, Res] { req =>
   timelineSvc(req.userId) flatMap {tl =>
    val tweets = tl map tweetSvc.getById(_)
    Future.collect(tweets) map tweetsToJson(_) }
    }
} //#3
Http.serve(":80", authFilter andThen apiService) // #4
// #1 创建每个服务的客户端
// #2 创建过滤器来认证传入的请求
// #3 创建服务,将已认证的时间线请求转换为json响应
// #4 使用认证过滤器和服务,在80端口启动新的HTTP服务器

 在上面的例子中,创建了时间线服务、Tweet服务以及认证服务的客户端,创建了一个Filter来认证原始的请求,最后,实现了服务,将其与认证过滤器组合起来,并暴露在80端口上。
        当收到请求时,认证过滤器会尝试进行认证,如果失败的话就会立即返回并不会影响到核心服务。认证成功后,AuthReq将会发送到API服务上。服务会使用附带的userId借助时间线服务查找该用户的时间线,这里会返回一个tweet id的列表,然后对其进行遍历获取关联的tweet,最终请求的tweet列表会收集起来并作为JSON返回。在这里我们将并发的事情全部留给了Finagle处理,并不用关心线程池以及竞态条件的问题,代码整洁清晰并且很安全。
        以上介绍了Finagle的基本功能以及简单的用法。Finagle支撑了Tweet巨大的网络传输增长,同时还降低了延迟以及对硬件的需求。目前,Finagle与Netty社区积极合作,在完善产品的同时,也为社区做出了贡献。Finagle内部会更加模块化,从而为升级到Netty 4铺平道路。

本文转载自:http://www.infoq.com/cn/news/2014/05/twitter-finagle-intro

共有 人打赏支持
tqyin
粉丝 24
博文 76
码字总数 35391
作品 0
成都
高级程序员
分布式服务框架

转公司wiki博客. 分布式服务框架是面向服务架构的基石,是解耦子系统的利刃。核心实现是RPC(远程过程调用),但又不仅限于RPC,因为一个系统的高效、稳定、可靠的运行还需要依赖于服务管理发...

郭恩洲_OSC博客 ⋅ 2015/09/11 ⋅ 0

Twitter zipkin 分布式跟踪系统的设计与实现

概述 Twitter的zipkin是一个致力于收集Twitter所有的分布式服务的时间数据的分布式跟踪系统。它提供了收集数据,和查询数据两大服务。系统的理论模型来自于Google Dapper 论文。Dapper这篇论...

tqyin ⋅ 2016/09/28 ⋅ 2

互联网公司的RPC框架如何选择?

主流RPC框架 RPC框架比较 语言 协议 服务治理 社区 机构 Hessian 多语言 hessian – 不活跃 Caucho Thrift 多语言 thrift – 活跃 Apache Finagle Java/Scala 多协议 支持 活跃 Twitter TCha...

王爵nice ⋅ 2016/02/03 ⋅ 17

RPC系统--Finagle

Finagle 是一个容错的、与协议无关的用于JVM 的RPC系统。Finagle 使用 sbt 进行构建。Finagle 来自 Twitter !它使得在 Java、Scala 或任何基于 JVM 的语言重构建鲁棒的客户端和服务器非常容...

匿名 ⋅ 2012/10/09 ⋅ 1

开源公司黄页之 Twitter 开源软件推荐

从Twitter的GitHub账户中可以看到,Twitter已经开源的开源项目有近200个,领域涉及分布式架构、大数据、异步网络传输(客户端、服务端)、Web、工具等。Twitter可以称为构建于开源项目之上,...

oschina ⋅ 2016/07/06 ⋅ 8

Undertow、Vert.x 和 Netty 的压力测试比较

Tech Empower 对 90 个框架进行了性能方面的比较测试,这些测试主要是设计一些基础的任务,例如 JSON 序列化、数据库访问、服务端模板生成等等。你可以从 introduction, permutation 和 envi...

oschina ⋅ 2014/02/25 ⋅ 19

基于微服务的原生云应用开源“服务网格”项目--Linkerd

Linkerd 是一个提供弹性云端原生应用服务网格(service mesh)的开源项目,也是面向微服务的开源 RPC 代理。它的核心是一个透明代理。 linkerd(发音 "linker-DEE")是一个透明的服务网格,旨...

匿名 ⋅ 2017/04/26 ⋅ 1

Twitter-Finagle使用ZK作为service discovery

http://blog.oskarsson.nu/post/40196324612/the-twitter-stack 需要翻墙! 中文版: http://www.csdn.net/article/2012-02-14/311806 http://www.javaflush.com/twitter%E7%9A%84%E5%BC%80......

强子哥哥 ⋅ 2016/06/05 ⋅ 0

当当网开源 Dubbox,扩展 Dubbo 服务框架支持REST风格远程调用

当当网近日开源了Dubbox项目,可为Dubbo服务框架提供多项扩展功能,包括REST风格远程调用、Kryo/FST序列化等等。 当当网架构部和技术委员会架构师沈理向InfoQ中文站介绍了Dubbox项目,开发背...

空云万里晴 ⋅ 2014/10/23 ⋅ 13

服务框架和软件框架

一.服务框架 服务框架基于业务对应用SaaS分发模式的服务进行整合,以产生新的应用,其具有如下的特点: ü 它是面向特定领域的可复用软件集成平台; ü 反映了该领域应用的一般需求和结构; ...

郭恩洲_OSC博客 ⋅ 2015/09/10 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

用ZBLOG2.3博客写读书笔记网站能创造今日头条的辉煌吗?

最近两年,著名的自媒体网站今日头条可以说是火得一塌糊涂,虽然从目前来看也遇到了一点瓶颈,毕竟发展到了一定的规模,继续增长就更加难了,但如今的今日头条规模和流量已经非常大了。 我们...

原创小博客 ⋅ 32分钟前 ⋅ 0

MyBatis四大核心概念

本文讲解 MyBatis 四大核心概念(SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper)。 MyBatis 作为互联网数据库映射工具界的“上古神器”,训有四大“神兽”,谓之:Sql...

waylau ⋅ 51分钟前 ⋅ 0

以太坊java开发包web3j简介

web3j(org.web3j)是Java版本的以太坊JSON RPC接口协议封装实现,如果需要将你的Java应用或安卓应用接入以太坊,或者希望用java开发一个钱包应用,那么用web3j就对了。 web3j的功能相当完整...

汇智网教程 ⋅ 今天 ⋅ 0

2个线程交替打印100以内的数字

重点提示: 线程的本质上只是一个壳子,真正的逻辑其实在“竞态条件”中。 举个例子,比如本题中的打印,那么在竞态条件中,我只需要一个方法即可; 假如我的需求是2个线程,一个+1,一个-1,...

Germmy ⋅ 今天 ⋅ 0

Springboot2 之 Spring Data Redis 实现消息队列——发布/订阅模式

一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式,这里利用redis消息“发布/订阅”来简单实现订阅者模式。 实现之前先过过 redis 发布订阅的一些基础概念和操...

Simonton ⋅ 今天 ⋅ 0

error:Could not find gradle

一.更新Android Studio后打开Project,报如下错误: Error: Could not find com.android.tools.build:gradle:2.2.1. Searched in the following locations: file:/D:/software/android/andro......

Yao--靠自己 ⋅ 昨天 ⋅ 0

Spring boot 项目打包及引入本地jar包

Spring Boot 项目打包以及引入本地Jar包 [TOC] 上篇文章提到 Maven 项目添加本地jar包的三种方式 ,本篇文章记录下在实际项目中的应用。 spring boot 打包方式 我们知道,传统应用可以将程序...

Os_yxguang ⋅ 昨天 ⋅ 0

常见数据结构(二)-树(二叉树,红黑树,B树)

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides 相关命题的证明可参考《算法(第...

浮躁的码农 ⋅ 昨天 ⋅ 0

android -------- 混淆打包报错 (warning - InnerClass ...)

最近做Android混淆打包遇到一些问题,Android Sdutio 3.1 版本打包的 错误如下: Android studio warning - InnerClass annotations are missing corresponding EnclosingMember annotation......

切切歆语 ⋅ 昨天 ⋅ 0

eclipse酷炫大法之设置主题、皮肤

eclipse酷炫大法 目前两款不错的eclipse 1.系统设置 Window->Preferences->General->Appearance 2.Eclipse Marketplace下载【推荐】 Help->Eclipse Marketplace->搜索‘theme’进行安装 比如......

anlve ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部