采访Eberhard Wolff:使用vert.x创建高容量、可伸缩的架构
InfoQ采访了Eberhard Wolff,讨论了这两种技术之间的区别、基于它们创建架构出现的挑战以及这些架构所提供的好处。
Eberhard之前是SpringSource的首席顾问,现在是adessoAG的架构和技术经理。他曾经从事分析和设计企业系统多年,并在过去几个月中对vert.x进行了研究。
InfoQ:Node.js现在已经出现有四年多了,vert.x出现了大约两年,很显然,对于这些技术和概念有很确定的需求。你对Node.js和vert.x持何种观点? 在哪里应用这些技术会很有用呢? 它们是为何种用例创建的?
Eberhard Wolff:我认为,对于在高性能的环境中处理大量客户端,有越来越大的挑战。异步的模型会对此有所帮助——它可以使用较少线程来处理很多客户端。但vert.x不仅限于此。在JVM上,你通常在一个应用程序中只能使用一种编程语言。Java还占主流,是大家比较喜欢的编程语言。然后,还有Scala,它有自己的框架,比方说Clojure。这和.NET上的情况有所区别。从最开始,.NET就定位于多语言——即便是在同一个应用程序中。vert.x让不同的语言可以更容易地在JVM中协作。在每种语言中,还有使用了各种框架的包装器。你可以基于vertx.的事件总线(event bus)在JVM上运行JavaScript代码,并传递JSON对象,你可以把它与不同的语言——像Java和Scala——集成。这是一种很有趣的变革,因为那是我所知的在JVM上唯一真正实现多语言的方法。然后,还有关于JVM上与模块化相关的挑战:你如何让单独的模块可以分开部署? 一种可能是使用OSGi。然而,它还没有得到广泛的应用。另一种方式是在一台服务器上部署不同的WAR文件,并让它们通过SOAP通信,但那是一种非常复杂的解决方案,而且会带来很大的性能负载。vert.x提供了一种非常棒的模块化方法。所以vert.x是一种非常有趣的框架,因为它是异步的、多语言的和模块化的。
InfoQ:说到Node.js,我真的在编写一个应用程序,它会运行在Node.js服务器上。它对请求以单线程的方式做出响应,并能够把那些请求取到线程池中, 在需要的时候做后续处理。那意味着我不需要任何模块化或者不同的组件作为高层次的概念,它们相互之间就可以通信。而vert.x在此是如何工作的呢?
Eberhard:在vert.x中,模块——各种不同的部署单元——会使用事件总线来相互发送事件。那些事件可以包含JSON之类的对象。vert.x中的模块拥有分离的类载入程序(classloader),所以它们可以真正被视为单独的组件。所以你可以把功能放到不同的模块中。例如,在Github上针对与MongoDB交互提供了一个模块, 它会使用来自于事件总线合适的事件,并把数据存储到数据库中。与异步编程相比,乍一看,它像是一种很小的特性,但是我认为它非常重要。它解决了你在企业应用程序中经常需要面对的问题。
InfoQ:你是否可以更详细地介绍一下事件总线? 我是否可以认为它是一种带有主题(topic)和队列的Java消息系统?
Eberhard:它不仅提供了点到点的通信,还提供了发布/订阅机制。它还可以分布在多个节点之上。然而,事件是短暂的。它们没有存储在任何一种稳定的存储介质上。因此,事件可能会丢失。这与JMS不同,后者把重点放在可靠性上,而vert.x的事件总线把重点放在性能上。
InfoQ:那些模块彼此之间的独立性如何? 你能否真的选择一种组件,并在运行的同时进行置换或升级? 针对我的模块的消息是否会一直排队,直到得到支持并运行?
Eberhard:模块可以在运行同时被置换或升级。但事件并不是持久的。所以,只要接收程序不存在,那么事件就会被抛弃。
InfoQ:在这种环境中使用的系统看起来会是什么样子? 毕竟,我不能说,一方面我想要高性能和高可伸缩性,另一方面我要集成无法符合这些概念的系统。
Eberhard:这正式vert.x的另一种优势:你可以定义一种叫做“垂直工件(worker verticles)”的东西。垂直工件会使用一个线程池,这样你可以集成需要阻塞IO的遗留代码。线程池与主事件循环(primary event loop)彼此分离。所以事件循环不会被垂直工件所阻塞。这样工件可以做它需要做的任何任务——同步的、阻塞IO的或者任何类型。这种特性真的非常重要,因为在Java领域有很多库,并没有被设计为非阻塞的。
InfoQ:当你设计一个新系统的时候,提前对这进行评估会带来多大好处? 我曾经读过对Node.js的创造者Ryan Dahl的采访,在其中他声称选择JavaScript是个好主意,因为它只有少数几个模块。而“只有少数几个模块”也意味着,从概念的角度来看,不会有太多错误的模块——也就是没有设计为异步使用的模块。我想,如果你选择一种任意的Java库,可能很快就会遇到麻烦,对吗?
Eberhard:没错。但那正是垂直工件起作用的地方。我理解Node.js的用意。只是因为JavaScript程序员习惯于使用异步概念。他们已经使用AJAX进行异步编程很长时间了。在Java社区中,还有——至少曾经有过——这样的声音,说“我们理解如何构建大型系统,而JavaScript程序员只是构建简单的前端程序”。但有了AJAX,JavaScript程序员们总是需要处理回调和异步的概念,那对他们来说很自然。因此,基于JavaScript来构建类似于Node.js之类的东西就非常自然了,况且我们拥有高性能的JavaScript环境。vert.x也显示了这一点:看一下vert.x的例子,在Java中,有匿名的内部类,而不像在JavaScript中是函数。那只会导致需要编写更多代码。依我所见,那是Java语言中一种经典的设计错误。匿名内部类的想法很不好。但Java 8会解决这个问题。
InfoQ:你提到,Java社区告诉JavaScript程序员“去构建前端程序”。在Node.js方面是否有少许劣势,因为有很多人能够使用JavaScript编程,但能够在Node.js上构建的系统确实需要不同的概念背景知识? 我们会有这样的风险,因为这些进入到企业市场中的新语言,而看到人们设计系统时不具备合适的企业知识?
Eberhard:让我来这么说吧:Java社区传统上是构建后端系统,并且用于大型系统上。大量设计大型系统的想法都来自于Java社区。但是我不会通过开发者碰巧使用的语言来对其加以判断。至于说异步的概念,它们已经深入到JavaScript开发者的血液之中了。对于大型系统来说,模块化是一个重要的话题。vert.x在此提供了一种有意思的替换方案。使用合适的模块化方法,你可以把大型系统分解为更小的部分。更有意思的是,模块化的概念基于消息,而消息又提供了更多优势。你可以达到更松的耦合,因为系统仅仅依赖于消息,而不是依赖于调用特定的方法。
InfoQ:是的。并且通过使用JSON会更方便,那样我们就拥有了一种消息格式,它本身就非常“松”,有助于避免兼容性的问题。
Eberhard:的确如此。但JSON只是一种技术可能性。JSON并没有提供任何模式的定义。实际上,有人说基模(schemata)不好,因为它让事情变得不够灵活。他们抱怨最终不得不使用瀑布模型,其中第一步你要设计接口,然后基于它们来构建你的系统。不管怎样,我认为那会很好,如果你能够检查JSON消息的结构,就会发现它正如我所愿。而那正是我意识到JSON的问题的地方。通常,你想要某种契约,其中定义了期望的数据结构。我认为,在这样的情况下,你会从XML基模受益。
InfoQ:实际上,几个月之前我在动态数据类型方面有过有趣的经历。我举办了一场培训,其中我们编写了复制Twitter的代码,基于PhoneGap、Node.js和MongoDB。忽然,前端人员有了个主意,要向他们的“tweets”里面增加图片。那些图片会通过Node服务器传输到数据库,稍后再回到前端。当前端团队展示这个特性的时候,节点和数据库团队都非常惊奇,因为他们没有意识到现在处理的是图片。在这种情况下,那非常酷,但我认为,这也会导致严重的问题。
Eberhard:那正是灵活的基模的优势所在。但如果两个以上团队参与到开发中,那么你通常会想要拥有某种接口契约。为了执行那种契约,你需要某种形式的模式(schema)。你所描述的情形表现出另一个有趣的问题:你需要在哪里解释数据? 在你提到的栈中,在大多数系统中,你只是对检查或者验证数据不感兴趣。你只是想要存储JSON文档,就是那样。文档中有什么并没有关系,因为你不需要解释数据的语法。可能在企业系统中有些不同。如果你说“那是客户”,那么你就需要知道,客户的数据看起来会是什么样子。
InfoQ:好吧,那背后有一些JavaScript和动态类型的观念。当它像鸭子一样走路、游泳、嘎嘎叫,那么我们就把它叫做鸭子。如果它看起来像是我们期望的类型,并且包含所有我需要的数据,那么我就不关心其他内容。
Eberhard:的确如此。但还有问题。当你获得一个customer对象,期望它拥有出生日期,那么你就可以在schema中定义。在那里XML基模要更好一些,因为它们拥有高度复杂的类型系统。所以你甚至可以用正则表达式的方式来定义一个订单号码看起来是什么样子。如果有人违反了schema,就会遇到问题。schema会提前告诉他的数据非法。它可以使用这作为早期的警告。
InfoQ:在vert.x事件总线中,一直使用的是JSON吗?
Eberhard:可以使用任意对象。然而,如果你使用Java对象,那么你就会遇到序列化和载入类的问题。那就是为什么在这种情况下JSON会更好一些,而那也是经常采用的方式。
InfoQ:关于像Node.js和vert.x这样的技术,你会说大型应用的架构基于这些技术,或者你会说一种架构只是在特定的领域使用这些技术? 据我所知,LinkedIn使用Node.js来处理移动设备的流量,但应用程序本身是“正常的”企业应用。
Eberhard:现在,我想还需要一段时间之后才能回答这个问题,直到那些技术在经典的企业系统中得到广泛应用。当前我能够看到它与Spring的区别。大家都知道Spring适合于企业系统。迁移路线很清晰。对于vert.x有些不同。vert.x可以嵌入在其他应用中。但是想要完全发挥它的能量,你需要使用它的运行时环境。这种环境与传统的企业级Java截然不同。只有一个Java进程,而没有servlet或者应用程序容器。然而,有一种使用异步系统的趋势。有Erlang、Scala和Akka语言,以及向Spring Integration和Apache Camel之类的框架——每一种都有不同的方法和不同的关注点。例如,Sring Integration和Apache Came提供了各种适配器,用来发送异步消息和处理数据。所以它们提供了集成解决方案——正如《企业集成模式》一书中所显示的。Erlang的思想是要实现高性能和可靠性。vert.x也类似。所以异步工作是构建系统的一种特殊方式,那会变得越来越重要。
InfoQ:一个人不应该试图使用这样的技术来实现,你是怎么想的? 这会遇到什么样的陷阱?
Eberhard:问题是,在何种情境中这些解决方案会特别有用。我认为甜蜜点是构建高性能的系统,特别是有非常大量的客户端的系统。或者是集成场景和其他能够受益于松耦合的场景。换句话说:如果你想要构建一个平常的web应用程序,那么它可能不是最佳的解决方案。
InfoQ:我如何才能在vert.x中使用现存框架? 我能否使用所有现存的框架,比方说使用某些前端框架,像Apache Wicket,来提交网页?
Eberhard:不幸的是,那并不容易,因为大多数前端框架都基于Servlet API。servlet API是阻塞性的,所以与vert.x不兼容。这样,你会使用一种模板引擎来设计解决方案,并自己来提交HTML页面。但更典型的用例是使用JSON和REST接口为JavaScript前端构建后端系统。你最后会在前端拥有更多逻辑,而在后端就会少一些。不一定是更少业务逻辑,可能是用于渲染HTML页面的逻辑。
InfoQ:你提到了生产环境,你是否认为在这种情境下会对云平台有更多应用? 或者不会有太大差别,但我们的运维团队需要采用新的范式?
Eberhard:那取决于在这里你所指的“云”是什么。大多数PaaS云的问题在于,它们只是提供了Servlet API,那样你就会面对之前讨论的问题。在IaaS上部署vert.x应用没有任何问题,但我并不认为vert.x在这个领域比其他Java技术有很大的优势。
InfoQ:让我们再多说一些与在开发期间提供支持的内容。如果我们知道Node.js,那么你就可以找到更好或者更差支持JavaScript的IDE,但不管怎样,看起来和我们开发Java软件时所使用的相差甚远。关于质量保证,看起来也是一样:有一些改进,但不是太大。对于vert.x情况如何?
Eberhard:vert.x最棒的地方在于,你可以使用热重部署(hot redeploy)机制。那会让开发更简单。而你还拥有vert.x能够编译和执行任何你提供的源文件的优势。然而,还没有任何Eclipse插件。
InfoQ:除此之外,我假设你可以使用所有语言表现出来的特性。例如,用于测试的基础架构,或者你决定对Java所使用的自动代码审查?
Eberhard:没错,你可以这么做。但正如我之前所说的,模块化和部署的概念是不同的,所以在此你会面临一些挑战。
InfoQ:关于在vert.x中调试会怎么样? 我们是否可以依附于运行中的进程?
Eberhard:没问题,那种特性是JVM所提供的。使用JVM还是带来不少好处的。代码会在执行比特码的高度优化的虚拟机上执行。我认为,为比特码创建优化的VM要比JavaScript容易得多。V8团队所构建的产品非常令人惊奇,且非常非常让人激动。但还存在大量在JVM上投入巨大的引擎。我认为依靠并使用它是个好主意。有些数据表明,vert.x在各种指标上都比Node.js快,但和其他评测数据一样,那很难解释。
InfoQ:的确如此。但听起来很符合逻辑。另外,V8团队在过去几年中所创造的东西非常棒。但另一方面,JVM开发已经有20多年的历史了。你不得不说,V8引擎也依赖于它的一些概念,从而达到高性能。但是,如果我不知道那些概念,也会编写出语法上正确的代码,那本身并没有问题,但那些内部概念会导致低性能。我认为在Java中并不会以同样的方式出现 。我可定会编写带有愚蠢的循环的丑陋代码,但那是另一种错误了。
Eberhard:但那就像是比较苹果和桔子。实际上,我宁愿与整个栈比较——Rhino和JVM在vert.x上JavaScript的表现,以及V8在Node.js上的表现。在JVM中,一开始就没有对动态语言的支持。另一件事是,你可以在vert.x上使用多事件循环,通常是每个CPU核心一个。那看起来V8是做不到的。因此,如果我拥有一台八核的服务器,那么就需要启动多态Node服务器,而对于vert.x来说只需要启动带有八个事件循环的JVM。那是不同的,可能会更有效率。但正如我之前所说,评测是一个复杂的话题。
InfoQ:在最后我再提一个问题,vert.x的主要好处是什么? 你不能说对这个话题不感兴趣。
Eberhard:好吧,有很多原因让我认为这是一种令人激动的技术,而且我认为它非常重要。首先,在将来对于JVM来说,是否有异步IO会很关键。然后,它拥有更好的模块化概念,而那正是我认为企业级Java所缺乏的。这对很多项目都是问题,很难得到好的解决方案。最后,我认为它至关重要,因为依我所见,JVM作为多语言的虚拟机会变得更重要。我不认为Java语言自身足以支撑JVM的未来,而vert.x会帮助它,因为它是真正多语言的。挑战是,对于vert.x运行时,全功能的servlet和应用程序容器需要交换。
Eberhard Wolff是Java Champions的创始成员,多篇文章和多本图书的作者,并且经常会在国际会议上发表演讲。他的背景在于企业Java、Spring、云和NoSQL。它在德国柏林为adesso AG工作,职位是机构和技术经理。
查看英文原文:High-Volume / Scalable Architectures with vert.x - interview with Eberhard Wolff