在当今应用架构里,分布式和应用与服务之间的通讯都是核心思想。想要从分布式中获益,你必须紧紧记住几条基本的原则,不然你可能会很容易遇到性能和扩展性问题。在开发阶段这些问题不会常常出现,但当你进行负载测试或产品化的时候,你可能会意识到你选择的软件架构不能知足性能和扩展性需求。在这篇文章中,咱们重点关注构建分布式应用须要记住的一些关键点。web
分布式须要应用之间进行交互。范围包括从大规模集群架构上简单的点到点的交互,到动态的面向服务或基于服务的架构。跨系统边界的通讯也是提升软件系统扩展性和可用性的关键。现在软件架构已把分布式做为一个核心的必要的概念。Java平台成为了核心的角色,由于它的分布式、有很好的API和产品支持这些特色。应用场景从像SAP这样在标准软件上的系统集成,到内部或外部的服务集成。SOA提供这样的方法,使服务和应用变的灵活和可重用,能够对新的市场需求很快的作响应。另外,像使用网格计算,虚拟机和多核刀片机的趋势,致使愈来愈多的集群应用的出现。这主要是因为追求高可扩展性和高可用性驱动的。并且云计算的发展趋势代表,分布式平台未来会更加流行。另外,系统正变得但愿能更动态的增长其灵活性。例如,在运行时添加应用节点。这些趋势也致使了系统结构变得愈来愈复杂。对于开发人员来讲,则更难理解产品中服务调用是如何实现的了。这种复杂性和缺少对相应知识的了解,很容易致使资源消耗的增长(CPU,内存,网络)和性能的下降。数据库
面具后的恶魔编程
现在,远程技术使分布式应用的实现更加简单。底层通讯的细节和服务端和客户端的基础结构对开发人员是透明的。如今,若是要把一个Java类暴露为一个服务,有时只须要简单的加一个注解到这个类上。服务也能够被工具生成的代理很容易的访问。以下图所示,可是,这仅仅是冰山的一角。缓存
远程堆栈的核心块是对象的序列化和传输的格式化。一般,应用的开发者不须要知道这些。可是,这也是不少性能问题产生的缘由。效率不高的序列化意味着,经过网络传输了不少不须要的数据。复杂对象的显示和大量的数据,在序列化和反序列化期间,致使CPU和内存的使用会很高。底层的基础架构和它的配置对应用的性能有很大的影响。在客户端,主要是链接的管理和底层线程模型。在分布式应用中使用链接的指导方针和数据库的链接很像。创建一个链接须要必定的时间。但这一样要看是什么协议。例如,创建一个HTTPS的链接的开销要大于一个简单的TCP/IP链接。同时,链接又是系统很重要的资源。因此,使用链接池很重要。正确的配置在这里也很关键,由于错误的配置文件给咱们带来的坏处要多于好处。线程的模型涉及到请求如何被处理。重要的是请求是被同步仍是异步处理。同步通讯阻塞一个进程直到收到相应。在异步通讯中,当收到响应时会调用一个回调。这就容许这个线程被其余事务使用。在服务端,可用的工做线程数量就是定义的并行处理的最大服务请求数。网络自己也是分布式应用的一个重要组件。网络是比影响性能更加限制其可扩展性的重要的瓶颈资源。这块一般在开发时会被忽视,由于没有调用实际的网络。网络
远程调用之美在于... 架构
这有不少能够选择,Java提供了很是多的可能性和技术来实现分布式应用。远程技术的选择对应用的架构、性能和扩展性有十分重要的影响。最“老的”的可是几乎是用的最广的远程协议是RMI(以下图)。异步
RMI是J2EE应用的一个标准协议。像它的名称暗示的同样,设计时就是为了调用远程Java虚拟主机上的对象提供的方法。对象在服务端被暴露出来,这时客户端就能够经过代理调用这个对象。一样的服务端对象被多个线程使用。线程池被RMI基础设施管理。通讯经过TCP/IP被处理,而且使用JRMP或针对RMI的基于IIOP GIOP(CORBA协议)的协议。应用服务端也提供本身的属性协议来优化其性能。如服务端的引用须要管理同样,RMI基础设施也提供了垃圾回收器来管理引用。这个分布式垃圾回收器(DGC)自己也使用RMI协议来管理服务器端的对象生命周期。除了客户端和服务端很强大,RMI还有一些其余的实现。关于RMI的详细介绍及应用请参考51CTO以前的文章《用RMI实现基于Java的分布式计算》。分布式
RMI只支持同步通讯,缺点上面已经讨论过了。另外,不能为数据驱动的服务提供低级缓存,由于它是基于2进制协议的。开发人员和系统架构可以改变基础设施的配置参数来优化性能。JMS是J2EE平台上使用的第二多的协议。以下图:
有别于RMI, JMS是一个异步的协议。通讯是基于队列的,以便监听器能够对消息做出反应。JMS不是一种标准的远程调用协议,可是它仍然可以知足服务与服务之间的交互。在SOA中很是重要的不少ESB的实现,就采用基于JMS的中间件来进行服务之间的信息传递。因为JMS是异步的,一些典型的同步问题就能够避免。在不少系统中,高可扩展性的关键在于可以很快的释放资源(像线程)。在不少状况下,异步处理是惟一合适的方法。JMS提供不少不一样的传输格式。XML是最通用的消息格式,但二进制格式也是可能的。消息结构的设计是应用架构的一个重要部分,由于它能够直接影响到应用的性能和可扩展性。
基于SOAP的WEB Service(以下图)和其余相关的WS-*也在Java 企业应用领域中变得愈来愈重要。
设计SOAP是为了替换CORBA,并且一开始就获得了业界的强烈支持。由于WS-I之间的共同努力,不一样平台差很少可以很容易的链接起来。SOAP是一种基于XML的RPC协议,因此很容易和浪费带宽联系到一块儿。
愈来愈多的基于REST的服务开始取代SOAP。Java中的REST服务在JSR 311中有说明,是基于HTTP所支持的基本操做而设计的。可是,REST不是做为RCP协议,而是面向资源的,为了访问和操做(web)资源而设计的。这两个协议都支持同步通讯。这也是底层HTTP协议所要求的。WS-地址对SOAP协议进行扩展,因此它也容许异步服务的实现。REST最大的优势是,可以很容易的经过HTTP代理实现缓存。REST依靠使用HTTP底层协议,不管如何都和用的机制。
可能犯的错
分布式应用的不少地方均可能出现潜在的问题,如图所示:
在客户端,主要的问题在于糟糕的交互设计-太多的服务调用,或者选择了错误的通讯模式。同步事务运行时间过长很容易致使性能问题。在通讯层,大量的数据和过多的服务调用所产生的高的网络负载是主要问题。在服务端,不适当的服务接口设计和不合适的序列化策略的使用致使性能和扩展性问题。咱们下面仔细看下这些问题。
分布式应用的问题原由
通讯协议的正确选择主要取决于系统的总体架构和底层的需求。若是你工做在有mainframe、Java和.NET组件相互交互的特异环境中,用SOAP是行不通的。在纯Java环境中,在JRMP上使用RMI还是性能最优,可扩展性最好的解决方案,你可以得到开箱即用的编程支持。在不少SOA实现中,SOA和基于Web Service的实现同义而语。因此有愈来愈多的使用SOAP做为RPC协议的纯Java应用案例出现,尽管采用这样的方法一点有点都没有。调查显示,和RMI-JRMP相比,常用SOAP仍是有意义的。除了描述过的标准协议,一些其余的基于XML的和二进制协议也在一些应用中使用。Hessian的性能就不错。另外,还有一些其余编程语言的实现。例如使用Spring把POJOs暴露给远程调用使不改变实现就在不一样的协议间切换变得相对容易。Spring 支持RMI, HTTP, Hessian, Burlap, JAX-RPC, JAX-WS 和 JMS。
反模式:饶舌的应用
在搭建分布式应用时,一个核心的原则就是尽可能减小远程调用。这些意味着数据序列化的开销,创建链接的开销和附件的网络负载。另外,CPU,内存和网络资源的消耗限制了可扩展性。因此,为远程应用的接口设计一种方法,来确保必要的服务交互数最小是十分重要的。尤为是那些起初是在本地搭建的,而后为了可扩展性缘由遭遇了大量服务交互的应用。这些问题大多会在负载测试或产品化时出现,但当本地开发测试时一点问题都没有。能够采用适当的性能管理方法,在开发过程当中分析远程行为就能够避免这些问题。下图显示的是一个经过dynaTrace分析一个应用的远程行为的例子 。
基于这个分析,接口可以从新建立,应用逻辑可以从新设计来减小远程调用的次数。可能的方法是,合并几个方法的逻辑为一个,或在几个调用请求周边的对象处,使用数据容器。特定数据的位置也能够帮助减小远程调用,由于在须要的地方数据是可用的。尤为当读数据时,使用cache能够很大程度上提升性能和可扩展性。在软件设计的早期,服务的分发和可能的通讯在成为需求或将成为需求时已经考虑到是很重要的。
反模式:大格式消息
当调用远程的服务时,这一般意味着数据会在不一样的协议上传输。像XML在SOAP协议上传输或二进制数据在RMI协议上传输。大多数技术传输对象的数据或对象自己。大多数状况下,序列化的发生是在远程实现的底层。序列化的开销和所传输对象的大小相对应。在实际状况下,咱们进行序列化的开销要占到98%。怎么会这样?一个鉴权服务接口须要一个用户对象来受权。这个用户对象不只有用户名和密码,还有不少属性,关联到其余用例的数据引用。标准的SOAP序列化要建立几千字节的数据消息。这些数据要被服务解析并映射到用户对象结构上,致使大量CPU和内存的消耗。解决方案再明显不过了。接口要重构,只须要用户ID和密码。因此,除了选择正确的远程技术,消息内容的设计对构建好的性能和可扩展性的应用很重要。一般正好符合设计的很好通常对象会带来高性能的回报。
反模式:分布部署
分布式的Java企业级应用会致使一个应用分割成多个服务和一些部署单元部署到一些应用服务上。分布式的有一个组件新的部署包不须要从新部署到其余组件上。另外一个可能性是,大量使用的服务可以部署到独立的硬件或被部署屡次。有大量部署单元的复杂应用,服务的交互变得愈来愈难理解。这会致使2个交互频繁的服务被部署到不一样的硬件上从而产生不少的远程调用状况出现。在大规模应用中,分析交互的频率和数据大小与部署结构一致是很重要的。不少时候,从分布式部署到本地可使性能获得很大的提高而不须要损失灵活性和可扩展性。尤为对那些无状态的服务,把它们部署到不一样的节点来提高其本地性。
结论
从这些反模式中能够看出,在应用的设计初期阶段就考虑可扩展性是很重要的。它是应用架构的一个关键驱动。在后期提升性能和可扩张性在多数或大多数状况下工做会越困难。对应用产品的详细分析来识别远程调用的频率或大致积数据,优化系统的一致性是不可或缺的。若是你遇到了类似的或不一样的问题,请让我知道,我好扩充个人反模式记录