JavaEE应用的可扩展性伸缩性

  本文讨论了JavaEE应用分布式扩展性的基础知识,scalability扩展性这个概念不是很容易理解,可能会被误解为高可用性High Availability(简称HA),有人建议使用集群clustering作为扩展性和HA的解决方案,这种说法其实没有错,关键是我们需要搞明白为什么是这样。

  扩展性并不是Java EE平台规范的标准组件,其实现技术和服务器供应商有关,实现一个JavaEE应用的可扩展性因此有点小困难,因为没有厂商资料或规范文档来指导。

  扩展性分纵向扩展Scale up和Scale Out水平扩展,首先我们需要明白它们的区别:

  • Scaling Up纵向扩展: 这意味着给一台服务器增加更多资源,比如RAM或磁盘空间 处理器等,在某些场景下是有用处的,但是过了某个奇点后就变得非常昂贵,这时最好是进行水平扩展Scaling Out
  • Scaling Out水平扩展: 可以增加更多服务器,也称为集群,因为所有服务器都以某个单元结成组进行集群工作。

  其次,我们要明白高可用性HA不代表扩展性,某个系统是高可用性意味着多个服务器中某个坏了不影响整个应用的正常运行,但是有可能它并不扩展,扩展性是提高系统的处理能力,比如用户数 吞吐量和性能等等,这些都是需要通过增加资源如内存处理或服务器才能达到,具备了扩展性,但不一定高可用性,如果某台服务器坏了,某个请求无法发往这台服务器进行处理,如何将这个请求发送到正常服务器进行处理,这才属于高可用性。

负载平衡

  当你进行水平扩展增加了服务器数量,这就形成了集群,在这些机器前面你需要放置一个负载平衡器,这样你可以在集群成员之间分发平衡工作负载。

负载平衡器

应用是无态或有态?

  如果你的应用是无态的,那么使用水平扩展很好没有问题,你的应用逻辑并不依赖服务器的状态处理请求,比如RESTful API,或者EJB的无态Bean。

  如果你的应用有类似HTTP会话对象,有态EJB,或会话范围的BEAN(CDI,JSF)?客户端必须将状态保存在服务器端,这些状态必须一致保持为了进行以后的请求处理,比如HTTP会话对象也许存储着用户的已经授权的状态信息,购物车信息等等。

  在一个水平扩展或集群的应用中,请求会不断依次被不同节点服务器服务,当一个请求在一个服务器上生成与客户端有关状态时,如果下一次请求被分发到其他数据库,而这个服务器没有没有之前的状态数据,怎么办呢?

  使用Sticky session,这可以在负载平衡器进行配置,确保某个客户端请求能够总是发送给同样的服务器,但是如果这台服务器崩溃了怎么办?服务器的状态被摧毁了。

复制集群

  为了解决服务器崩溃,状态丢失的问题,配置应用服务器集群支持有态组件的复制,这样可以确保HTTP的会话数据或服务有态数据能够一直保存着,这样终端用户请求可以被分发到任何服务器。

复制集群

  集群复制是你的Java EE容器或应用服务器的规范,可以查询厂商手册发现如何配置,大部分应用服务器都支持有态EJB的集群,但是现在又带来另外一个问题,每个应用服务器都能服务处理会话数据了,但是会耗费更多JVM heap空间,引起更多垃圾回收触发频率,这可以通过Heap外内存使用避免,或者使用专门的外部存储会话的数据库,比如Cassandra这样的NoSQL。

分布式缓存

  使用分布式缓存可以存储有态的数据在内存中,这些产品有Oracle Coherence, Hazelcast,这样服务器就不需要自己保存任何用户会话状态,重启维护都没有关系,将有态服务变成无态服务进行扩展。

  分布式不等于复制,在两者之间有区别:

  • 分布式: 缓存的成员共享数据,数据集在缓存集群节点之间切分分区保存。
  • 复制: 所有缓存节点都有数据,每个缓存服务器包含整个数据的拷贝。

 

构建可扩展的有态服务

扩展性专题