Thursday, January 03, 2008

关于负载均衡器

web应用服务器集群系统,是由一群同时运行同一个web应用的服务器组成的集群系统,在外界看来,就像是一个服务器一样。为了均衡集群服务器的负载,达 到优化系统性能的目的,集群服务器将众多的访问请求,分散到系统中的不同节点进行处理。从而实现了更高的有效性和稳定性,而这也正是基于Web的企业应用 所必须具备的特性。
  
  高可靠性可以看作为系统的一种冗余设定。对于一个特定的请求,如果所申请的服务器不能进行处理的话,那么其他的服务器能不能对之进行有效的处 理呢?对于一个高效的系统,如果一个Web服务器失败的话,其他的服务器可以马上取代它的位置,对所申请的请求进行处理,而且这一过程对用户来说,要尽可 能的透明,使用户察觉不到!
  
  稳定性决定了应用程序能否支持不断增长的用户请求数量,它是应用程序自身的一种能力。稳定性是影响系统性能的众多因素的一种有效的测量手段,包括机群系统所能支持的同时访问系统的最大用户数目以及处理一个请求所需要的时间。
  
  在现有众多的均衡服务器负载的方法中,广泛研究并使用的是以下两个方法:
  
  DNS负载平衡的方法RR-DNS(Round-Robin Domain Name System)
  负载均衡器
  以下,我们将就这两种方法进行讨论。
  
  DNS轮流排程 RR-DNS(Round-Robin Domain Name System)
  
  域名服务器(Domain Name Server)中的数据文件将主机名字映射到其IP地址。当你在浏览器中键入一个URL时(例如:www.loadbalancedsite.com), 浏览器则将请求发送到DNS,要求其返回相应站点的IP地址,这被称为DNS查询。当浏览器获得该站点的IP地址后,便通过该IP地址连接到所要访问的站 点,将页面展现在用户面前。
  
  域名服务器(DNS)通常包含一个单一的IP地址与该IP地址所映射的站点的名称的列表。在我们上面所假象的例子中,www.loadbalancedsite.com 这个站点的映射IP地址为203.24.23.3。
  
  为了利用DNS均衡服务器的负载,对于同一个站点来讲,在DNS服务器中同时拥有几个不同的IP地址。这几个IP地址代表集群中不同的机器, 并在逻辑上映射到同一个站点名。通过我们的例子可以更好的理解这一点,www.loadbalancedsite.com将通过下面的三个IP地址发布到 一个集群中的三台机器上:
  
  203.34.23.3
  
  203.34.23.4
  
  203.34.23.5
  
  在本例中,DNS服务器中包含下面的映射表:
  
  www.loadbalancedsite.com 203.34.23.3
  
  www.loadbalancedsite.com 203.34.23.4
  
  www.loadbalancedsite.com 203.34.23.5
  
  当第一个请求到达DNS服务器时,返回的是第一台机器的IP地址203.34.23.3;当第二个请求到达时,返回的是第二台机器的IP地址203.34.23.4,以此类推。当第四个请求到达时,第一台机器的IP地址将被再次返回,循环调用。
  
  利用上述的DNS Round Robin技术,对于某一个站点的所有请求将被平均的分配到及群中的机器上。因此,在这种技术中,集群中的所有的节点对于网络来说都是可见的。
  
  DNS 轮流排程的优势
  
   DNS Round Robin的最大的优点就是易于实现和代价低廉:
  
  代价低,易于建立。 为了支持轮流排程,系统管理员只需要在DNS服务器上作一些改动,而且在许多比较新的版本的DNS服务器上已经增加了这种功能。对于Web应用来说,不需要对代码作任何的修改;事实上,Web应用本身并不会意识到负载均衡配置,即使在它面前。
  简单. 不需要网络专家来对之进行设定,或在出现问题时对之进行维护。
  DNS 轮流排程的缺点
  
   这种基于软件的负载均衡方法主要存在两处不足,一是不实时支持服务期间的关联,一是不具有高可靠性。
[ refer to http://www.javaworld.com/cgi-bin/mailto/x_java.cgi]
  • No server affinity(姻亲关系:表示上一次请求送到了某个服务器,那么下个请求还是希望送到原来的那个服务器,否则有可能导致会话信息丢失) support. When a user receives an IP address, it is cached on the browser. Once the cache expires, the user makes another request for the IP address associated with a logical name. That second request can return the IP address of any other machine in the cluster, resulting in a lost session.
  • No HA support. Imagine a cluster of n servers. If one of those servers goes down, every nth request to the DNS server will go to the dead server.
  • Changes to the cluster take time to propagate to the rest of the Internet. Many corporations' and ISPs' DNS servers cache DNS lookups from their clients. Even if your DNS list of servers in the cluster could change dynamically, it would take time for the cached entries on other DNS servers to expire. For example, after a downed server is removed from your cluster's DNS list, AOL clients could still attempt to hit the downed server if AOL's DNS servers cached entries to the downed server. As a result, AOL users would not be able connect to your site even if other machines in the cluster were available.
  • No guarantee of equal client distribution across all servers in the cluster. If you don't configure cooperating DNS servers to support DNS load balancing, they could take only the first IP address returned from the initial lookup and use that for their client requests. Imagine a partner corporation with thousands of employees all pinned to a single server in your cluster!
  
   • 不支持服务器间的一致性。服务器一致性是负载均衡系统所应具备的一种能力,通过它,系统可以根据会话信息是属于服务器端的,还是底层数据库级别的,继而将 用户的请求导向相应的服务器。而DNS轮流排程则不具备这种智能化的特性。它是通过cookie、隐藏域、重写URL三种方法中的一种来进行相似的判断 的。当用户通过上述基于文本标志的方法与服务器建立连接之后,其所有的后续访问均是连接到同一个服务器上。问题是,服务器的IP是被浏览器暂时存放在缓存 中,一旦记录过期,则需要重新建立连接,那么同一个用户的请求很可能被不同的服务器进行处理,则先前的所有会话信息便会丢失。
  
  不支持高可靠性。设想一个具有N个节点的集群。如果其中的一个节点毁坏,那么所有的访问该节点的请求将不会有所回应,这是任何人都不愿意看到 的。比较先进的路由器可以通过每隔一定的时间间隔,对节点检查,如果有毁坏的节点,则将之从列表中去除的方法,解决这个问题。但是,由于在 Internet上,ISPs将众多的DNS存放在缓存中,以节省访问时间,因此,DNS的更新就会变得非常缓慢,以至于有的用户可能会访问一些已经不存 在的站点,或者一些新的站点得不到访问。所以,尽管DNS轮流排程在一定程度上解决了负载均衡问题,但这种状况的改变并不是十分乐观和有效的。
  除了上面介绍的轮流排程方法外,还有三种DNS负载均衡处理分配方法,将这四种方法列出如下:
  
  Ø Round robin (RRS): 将工作平均的分配到服务器 (用于实际服务主机性能一致)
  
  Ø Least-connections (LCS): 向较少连接的服务器分配较多的工作(IPVS 表存储了所有的活动的连接。用于实际服务主机性能一致。)
  
  Ø Weighted round robin (WRRS): 向较大容量的服务器分配较多的工作。可以根据负载信息动态的向上或向下调整。 (用于实际服务主机性能不一致时)
  
  Ø Weighted least-connections (WLC): 考虑它们的容量向较少连接的服务器分配较多的工作。容量通过用户指定的砝码来说明,可以根据装载信息动态的向上或向下调整。(用于实际服务主机性能不一致时)
  
  
  
  负载均衡器
  
  负载均衡器通过虚拟IP地址方法,解决了轮流排程所面临的许多问题。使用了负载均衡器集群系统,在外部看来,像是具有一个IP地址的单一服务 器一样,当然,这个IP地址是虚拟的,它映射了集群中的每一台机器的地址。所以,在某种程度上,负载均衡器是将整个集群的IP地址报漏给外部网络。
  
  当请求到达负载均衡器时,它会重写该请求的头文件,并将之指定到集群中的机器上。如果某台机器被从集群中移除了,请求不会别发往已经不存在的 服务器上,因为所有的机器表面上都具有同一个IP地址,即使集群中的某个节点被移除了,该地址也不会发生变化。而且,internet上缓存的DNS条目 也不再是问题了。当返回一个应答时,客户端看到的只是从负载均衡器上所返回的结果。也就是说,客户端操作的对象是负载均衡器,对于其更后端的操作,对客户 端来讲,是完全透明的。
  
  负载均衡器的优点
  
   • 服务器一致性. 负载均衡器读取客户端发出的每一个请求中所包含的cookies或url解释。基于所读出的这些信息,负载均衡器就可以重写报头并将请求发往集群中合适的 节点上,该节点维护着相应客户端请求的会话信息。在HTTP通信中,负载均衡器可以提供服务器一致性,但并不是通过一个安全的途径(例如:HTTPS)来 提供这种服务。当消息被加密后(SSL),负载均衡器就不能读出隐藏在其中的会话信息。
  
   • 通过故障恢复机制获得高可靠性. 故障恢复发生在当集群中某个节点不能处理请求,需将请求重新导向到其他节点时。主要有两种故障恢复:
  
  • 请求级故障恢复。当集群中的一个节点不能处理请求时(通常是由于down机),请求被发送到其他节点。当然,在导向到其他节点的同时,保存在原节点上的会话信息将会丢失。
  
  • 透明会话故障恢复。当一个引用失败后,负载均衡器会将之发送到集群中其他的节点上,以完成操作,这一点对用户来说是透明的。由于透明会话故障恢复需要节点 具备相应的操作信息,因此为了实现该功能,集群中的所有节点必须具有公共存储区域或通用数据库,存储会话信息数据,以提供每个节点在进行单独进程会话故障 恢复时所需要的操作信息。
  
   • 统计计量。既然所有的Web应用请求都必须经过负载均衡系统,那么系统就可以确定活动会话的数量,在任何实例访问中的活动会话的数目,应答的次数,高峰负 载次数,以及在高峰期和低谷期的会话的数目,还有其他更多的。所有的这些统计信息都可以被很好的用来调整整个系统的性能。
  
  负载均衡器的缺点
  
   硬件路由的缺点在于费用、复杂性以及单点失败的。由于所有的请求均是通过一个单一的硬件负载均衡器来传递,因此,负载均衡器上的任何故障都将导致整个站点的崩溃。
  
  HTTPS请求的负载均衡
  
   正如上面所提到的,很难在那些来自HTTPS的请求上进行负载均衡和会话信息维护处理。因为,这些请求中的信息已经被加密了。负载均衡器没有能力处理这类请求。不过,这里有两种方法可以解决这一问题:
  
  代理网络服务器
  硬件SSL解码器
   代理服务器位于服务器集群之前,首先由它接受所有的请求并对之进行解密,然后将这些处理后的请求根据头信息重新发往相应的节点上,这种方式不需要硬件上的支持,但会增加代理服务器的额外的负担。
  
   硬件SSL解码器,则是在请求到达负载均衡器之前,先经由它进行解密处理。这种方式比代理服务器的处理速度要快捷一些。但代价也高,而且实现比较复杂。

Reverse Proxy(就是对请求方显示单独的接口,对web server也是一样,但一般会携带原始请求的ip)

A reverse proxy or surrogate is a proxy server that is installed within the neighborhood of one or more servers. Typically, reverse proxies are used in front of Web servers. All connections coming from the Internet addressed to one of the Web servers are routed through the proxy server, which may either deal with the request itself or pass the request wholly or partially to the main web servers.

A reverse proxy dispatches in-bound network traffic to a set of servers, presenting a single interface to the caller. For example, a reverse proxy could be used for load balancing a cluster of web servers. In contrast, a forward proxy acts as a proxy for out-bound traffic. For example, an ISP may use a proxy to forward HTTP traffic from its clients to external web servers on the internet; it may also cache the results to improve performance.

Wednesday, January 02, 2008

(转)J2EE集群的神话

http://www.theserverside.com/tt/articles/article.tss?l=J2EEClustering

失效转移可以完全避免错误——否定

Jboss的文档中,整个章节都在警告你“你真的需要HTTP会话的复制吗?”。是的,有时没有失效转移的高可用性的解决方案也是可接受并且是廉价的。失效转移并不是你想象的那么强壮。

那么失效转移到底给你带来了什么?你可能想失效转移可以避免错误。你看,没有会话的失效转移,当一个服务器实例失效后,会话数据将丢失而导致错误。通过失效转移,会话可以从备份中恢复,而请求可以被其他服务器实例所处理,用户根本意识不到失效。这是事实,但这是有条件的!

回想一样我们定义的“失效转移”,我们定义了一个条件是失效转移是在“两个方法调用之间”发生的。这是说如果你有两个连续的对远程对象的方法调用,失效转移只会在第一调用成功后并且第二调用的请求发出前才能发生。

这样,当远程服务器在处理请求的过程中失效了会发生什么呢?答案是:多数情况处理将会停止而客户端将会看到错误信息。除非这个方法是等幂的(Idempotent),只有这个方法是等幂的,一些负载均衡器更智能,它会重试这些方法并将它失效转移到其他实例上。

为什么“等幂”重要呢,因为客户端不知道当失效发生的时候请求被执行到什么地方。是才刚刚初始化还是差不多就要完成了?客户端没法判断!如果方法不是等幂的,在相同方法上的两次调用可能会两次修改系统的状态,而使得系统出现不一致的情形。

你可能想所有在事务中的方法都是等幂的,毕竟,如果错误发生,事务将被回滚,事务状态的改变都将被复位。但事实上事务的边界可能不包括所有的远程方法调用过程。如果事务已经在服务器上提交了而返回给客户端时网络崩溃怎么办呢?客户端不知道服务器的事务是否是成功了。

在一些应用程序中,将所有的方法都做成等幂的是不可能的。这样,你只能通过失效转移减少错误,而不是避免它们。拿在线商店为例,假设每台服务器可以同时处理100个在线用户的请求,当一台服务器失效了,没有失效转移的解决方案将丢失100个用户的会话数据并激怒这些用户。而有失效转移的解决方案中,当失效发生的时候有20个用户正在处理请求,这样20个用户将被失效激怒。而其他80个用户正处于思考时间或在两个方法调用之间,这些用户可以透明地获得失效转移。这样,你就需做以下的考虑:

l 激怒20个用户和激怒100个用户造成影响的区别。

l 采用失效转移和不采用失效转移产品成本的区别

独立应用可以透明的迁移到集群结构中——否定

尽管一些供应商宣称他们的J2EE产品有这样的灵活性。不要相信他们!事实你要在开始系统设计时就要准备集群,而这将影响开发和测试的所有阶段。

Http Session

在集群环境中,如我前面提到的,使用HTTP Session有很多限制,这取决于你的应用程序服务器采用了那种会话失效转移的机制。第一个重要的限制就是所有保存的HTTP Session中的对象必须是可序列化的,这将限制设计和应用程序结构。一些设计模式和MVC框架会用HTTP Session保存一些不序列化的对象(如ServletContextEJB本地接口和WEB服 务引用),这样的设计不能在集群中工作。第二,对象的序列的反序列化对性能的影响很大,特别是数据库保存的方式。在这样的环境中,应该避免在会话中保存大 的或是众多的对象。如果你采用了内存复制的方式,如前所述你必须小心在会话中属性的交叉引用。其他在集群环境中的主要区别是在会话不管任何属性修改,你必 须调用“setAttribute()”方法。这个方法调用在独立的系统中是可选的。这个方法的目的是区别已修改的属性和那些没用到属性,这样系统可以只为失效转移备份必要的数据,从而提高性能。

缓存

我经历过的大多数J2EE项目都用了缓存来提高性能,同时流行的应用程序服务器也都提供了不同程度的缓存用来加快应用程序的速度。但这些缓存都是为那些典型的独立环境设计的,只能在单JVM实例中工作。我们需要缓存是因为一些对象很“重”,创建它需花费大量的时间和资源。因此我们维护了对象池用于重用这些对象,而不需要在后面创建。我们只有当维护缓存比创建对象更廉价时才能获得性能的提高。在集群环境,每个JVM实例都要维护一份缓存的拷贝,这些拷贝必须同步以维持所有服务器实例状态的一致性。有时这种类型的同步会比没有缓存带来更糟的性能。

Static变量

当我们设计J2EE应用程序时,在架构上经常会使用一些设计模式。这些如“Singleton”的设计模式会用到静态变量来在多对象之间共享状态。这种方式在单服务中工作得很好,但在集群环境将失效。集群中的每个实例都会在它的JVM实 例中维护一份静态变量的拷贝,这样就破坏了模式的机制。一个使用静态变量的例子就是用它来保持在线用户数。用静态变量来保存在线用户数是一个很简单的办 法,当用户进入或离开时就增加和减少它。这种方式在单服务器中绝对是好的,但在集群环境将失效。在集群中更好的方式是将所有状态保存到数据库。

外部资源

尽管J2EE规范不支持,但为各种目的仍然会用外部I/O的操作。例如,一些应用会使用文件系统来保存用户上传的文件,或是创建一个动态配置的XML文件。在集群环境是没有办法来在其他实例之间来复制这些文件的。为了在集群中工作,办法是用数据库作为外部文件的存放点,另外也可以使用SAN(存储区域网,Storage Area Network)作为存放点。

特殊服务

一 些特殊的服务只在独立的环境中才有意义,定时服务就一个很好例子,这种服务可以在一个固定的间隔时间有规律的触发。定时服务常用于执行一些自动化管理任 务。如日志文件滚动,系统数据备份,数据库一致性检查和冗余数据清理等。一些基于事件的服务也很难被迁移到集群环境中。初始化服务就是个好例子,它只在整 个系统启动时才发生。邮件通知服务也一样,它在一些警告条件下触发。

这些服务是被事件而不是被请求触发的,而且只被执行一次。这些服务使得负载均衡和失效转移在集群中没多少意义。

一些产品准备了这些服务,如Jboss使用了“集群单例设施”来协调所有实例,保证执行这些服务一次且仅有一次。基于你所选择的平台,一些特殊的服务可能会是把你的应用迁移到集群结构中的障碍。

分布式结构比并置结构更灵活——不一定

J2EE技术,尤其是EJB,天生就是用来做分布式计算。解耦业务功能,重用远程组件,这些使得多层应用非常流行。但是我们不能将所有的东西都分布。一些J2EE架构师认为Web层与EJB层并置得越近越好。这些计论后面会继续。先让我解释一下。

20 分布式结构

如图20所示,这是一个分布式结构。当请求来了,负载均衡器将请求分发到不同服务器中的不同WEB容器,如果请求包含了EJB调用,WEB容器将重发EJB调用到不同的EJB容器。这样,请求将被负载均衡和失效转移两次。

一些人看分布式结构,他们会指出:

l 第二次负载均衡没有必要,因为它不会使任务分配更平坦。每个服务器实例都有它们自己的WEB容器和EJB容器。把EJB容器用来处理来自其他实例WEB容器的请求比只在服务器内部调用并没有什么优势。

l 第二次失效转移没有必要,因为它不能是高可用性。多数供应商实现J2EE服务器都会在同一服务器中运行的WEB容器和EJB容器放在一个JVM实例中。如果EJB容器失效了,多数情况下在同一个JVM中的WEB容器也将同时失效。

l 性能将下降。想像一下对你的应用的一次调用包含一组对EJB的调用,如果你负载均衡了这些EJB,这将跨越每个服务器实例,导致许多不必要的服务器到服务器的交互。还有,如果这个方法在事务范围内,那么事务边界将包含许多服务器实例,这将严重影响性能。

实际上在运行期,多数的供应商(包括Sun JESWebLogicJboss)都会优化EJB调用机制,使请求首先选择同一个服务器中的EJB容器。这样,如图21所示,我们只在第一层(WEB层)做负载均衡,然后调用相同服务器上的服务。这种结构我们称之为并置结构。技术上说,并置结构是分布式结构的一种特例。

21 并置结构

一个有趣的问题是,既然多数的部署在运行期都演进成了并置结构,为什么不用本地接口代替远程接口,这将大提高性能。你当然可以,但是记住,当你使用本地接口后,WEB组件和EJB耦合得很紧,而方法调用也是直接的而不通过RMI/IIOP。负载均衡和失效转移分发器没有机会介入本地接口调用。“WEB+EJB”整体处理负载均衡和失效转移。

但不幸的是,在集群中使用本地接口在多数J2EE服务器中有局限性。使用本地接口的EJB是本地对象,是不可序列化的,这一个限制就使本地引用不能保存在HTTP Session中。一些产品,如Sun JES,会将本地接口区别看待,使它们可以序列化。这样就可以用在HTTP Session中。

另一个有趣的问题是,既然并置结构这么流行并且有好的性能,为什么还要分布式结构呢?这在多数情况下是有道理的,但有时分布式结构是不可替代的。

l EJB不仅被WEB容器使用,富客户端也会使用它。

l EJB组件和WEB组件需在不同的安全级别上,并需要物理分离。这样防火墙将被设置用于保护运行EJB的重要机器。

l WEB层和EJB层极端不对称使得分布式结构是更好的选择。比如,一些EJB组件非常复杂并且很消耗资源,它们只能运行在昂贵的大型服务器上,另一方面,WEB组件(HTMLJSPServlet)简单得只需廉价的PC服务器就能满足要求。在这种情况下,专门的WEB服务器可以用来接受客户端连接请求,很快处理静态数据(HTML和图像)和简单的WEB组件(JSPServlet)。大型服务器只被用来做复杂计算。这将更好的利用投资。

结论

集群与独立环境不同,J2EE供应商采用不同的方法来实现集群。如果你的项目为做到高伸缩性而使用集群,你应该在你的项目开始的时候就做准备。选择符合你的需求的正确的J2EE产品。选择正确的第三方软件和框架并确保它们能支持集群。最后设计正确的架构使得能从集群中受益而不是受害。


AppendixA: Classical cluster topology

The image “http://www.javaworld.com/jw-02-2001/images/jw-0223-extremescale7.gif” cannot be displayed, because it contains errors.


=================================================

ADDITIONAL INFORMATION

=================================================

垂直伸缩(scale vertically),应用部署在单个节点上,随着系统用户数的增多,可以增加系统的硬件资源,比如增加cpy,增加内存,增加带宽等。

水平伸缩(scale horizontally),应用可以部署在多个节点上,随着系统用户的增多,可以增加更多的处理节点,比如从一台节点增加到三台节点。 水平伸缩方式需要在每个节点上部署相同的应用,但是这种伸缩方式更加便宜,同时也提高了系统的可用性。

参考资料:http://www.theserverside.com/tt/articles/article.tss?l=ScalingYourJavaEEApplications