集群相关

来源:互联网 发布:linux安装phpmyadmin 编辑:程序博客网 时间:2024/06/07 01:05

需要从有状态和无状态两个方面着手处理集群
有状态的应用属于并发,如何在集群下提高并发性能有一套方法。
无状态的应用属于并行,让并行自由奔跑。无状态的服务组件就不需要考虑集群


在集群部署的情况下,应用程序需要做出调整,主要集中在以下方面:
对httpsession的处理.
I.数据库方式备份 session 数据必须是可序列化的 一般推荐不要在 session 中存放过大过多的数据,因为数据库的事务过程相当费资源
II.内存拷贝方式
III. Tomcat的实现:多服务器之间互相拷贝内存
IV.Weblogic, Jboss 和 WebSphere 的实现:双服务器拷贝
V. IBM 的方案:中央服务器
VI. Sun 的实现:专用数据库

对缓存的处理
我们引用分布式缓存或者集中式缓存,分布式缓存有很多种如ehcache,jbosscache,oscache等,集中式缓存比较典型的就是 memcached。

集群共享的文件系统
    1、共享目录,都mount同一个地方,简单方便的做法;
    2、存放在数据库里,需要改动代码,性能上面也低。
    3、把那个功能独立成一个应用, 不部署在集群上了(前提能够满足业务这样做当然好,不过要用到额外的网络资源)
    4、2也可以通过向一个专门的server请求获得锁来控制

定时任务
    Quartz支持集群环境下的Task,原理就是用数据库记录Task并进行同步。
    
synchronized关键字的失效。
1,线程、定时器,如:单机环境下我们写一个单线程,如用来像其他系统上传数据(这个数据部允许多次上传),如果程序发布在集群环境下回发生什么情况?呵呵。变成多线程了。多个计算机内的单线程同时上传数据。所以这个时候考虑的地方就很多了!!是选择故障转移还是负载均衡都是考虑的范围。定时器也雷同。可以参考spring quartz 集群解决方案。
2,Teraccotta
3,自己开发一个 central service, 提供lock, 其他tomcat实例都连接那个central service,保证唯一就可以,随便你用虚拟锁还是数据库锁

JDBC
JDBC注意的是数据库集群的情况,如weblogic编写程序时,获得JDBC链接可以这样写,
Connection con = null;
try {
    Class.forName("oracle.jdbc.driver.OracleDriver"); //JVM加载驱动类
    String url = "jdbc:oracle:thin:@(description =
        (address =(protocol = tcp)(host = 192.168.1.246)(port = 1521))
        (address =(protocol = tcp)(host = 192.168.1.248)(port = 1521))
        (load_balance= yes)(connect_data =(service_name = twjk)))";
    con = DriverManager.getConnection(url, "twjkdev", "twjk");
} catch (Exception e) {}
在服务器中配置数据源是不同的服务器配置也不一样。
如在websphere中,配置数据源的URL选项可以加入如下代码
jdbc:oracle:thin:@(description=(ADDRESS_LIST =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 10.11.1.158)(PORT = 1521))
    (ADDRESS = (PROTOCOL = TCP)(HOST = 10.11.1.159)(PORT = 1521))
    (load_balance=yes)(failover=yes))(connect_data=(service_name= racdb)))

在Hibernate中,只有两个地方和集群有关系:一个是主键生成策略,有些主键生成策略,如increment,生成id的唯一性依赖于单个JVM去访问该表,另一个是对PO使用了支持集群的Cache,此时需要注意让PO实现Serializable接口。此外,Hibernate和集群并没有什么关系。

Shared Nothing Architecture
以往集群架构都采用Session共享模式进行设计,而后PHP等方面提出了SNA架构,主张Session不共享。SNA架构思想,无论对企业应用还是大型互联网站,极大提高了web应用的吞吐量和性能。
一般SNA架构以集成分布式Cache例如 memcached 的方案居多,此处姑且称为 Cache模式。
SNA思想的关键就是每个集群内web server实例不互相共享session,Cache模式主张session数据都放到分布式缓存中,意味着,逻辑上集群内还是要共享session信息;这种考虑源于负载均衡时,同一个IP发来的两个请求,可能走到不同的 Web Server上。因此,只要同一IP的两个请求转发到同一个 Web  server实例,那么就可以不需要全局的 session信息缓存。
1) 采用 F5硬件负载均衡器,使用IP记忆机制实现了这一点(同一IP的请求转发到同一个 Web server)。因此,各 web server实例的session无需共享,仍然保存在自己的session内存中,节省了网络开销和Cache命中查找时间。F5很贵,因此对于网站一般负担不起,但可以采用软件负载来做到这一点。
切分模式的SNA架构:
2) IP Memory(IP记忆):负载服务器记录 客户端IP -> ServerID 的关系,模拟F5;
3) (Dispatch by Rule)按规则转发:IP记忆需要维护一张路由table, 因此,需要消耗一定内存,以及映射关系查找的时间;
   我们将客户端的所有IP看作一个集合 IP Set,按固定规则将其平均分配集群的server实例上去,这样就可以节省路由table的开销。 关键是分配算法,可以考虑的有:
   2.1) 简单数值法: IP各节加总 = X, 假定集群实例个数为 N,编号1-N, 那么每次请求选择的目标server id = X mod N。
   2.2) hash值法: 有的系统可能想基于 userid 进行请求分配, 那么可以采用 X = hashCode(userid), serverID = X mod N;
   具体情况下, 可以灵活选择使用那个数据项判断请求分配的逻辑。这个思想参考了  memcached 的集群管理思想。
4) stickySession方式。
    一般apache等采用这种方式做负载均衡。但必须结合 jvmRoute。 第一次被分配的 web server必须返回一个 jvmRoute在response中,并由 apache 送到客户端浏览器,第二次请求发起时,request信息中将包含 JSESSIONID 和 对应的 jvmRoute, apache根据次找到对应的 server,完成 stickySession机制。
切分模式的SNA架构,基于规则进行请求转发,可以省去分布式Cache的使用,更进一步的提升系统吞吐量和响应性。

无共享的集群架构(SNA),在这样的集群中,每个节点具备完全相同的功能,并且不需要知道其他节点存在与否。每个节点JVM进程不保持全局状态,才能够保证n个JVM节点的幂等性,那些所有涉及到全局状态的,必须放在JVM进程之外,例如用户ID可以使用cookie,session可以放入数据库(这并不是一个好的选择),文件可以放在共享存储系统中。
也就是说httpsession的信息需要被保存在JVM进程之外,例如分布式缓存、数据库。

Terracotta
一个JVM级的开源群集框架,提供:HTTP Session复制,分布式缓存,POJO群集,跨越群集的JVM来实现分布式应用程序协调.它通过在应用下面的堆级别进行集群而不是直接集群应用.(采用代码注入的方式,所以你不需要修改任何)。
一般利用Tomcat搭建Web应用集群有如下几种方法:
1、利用负载均衡器的粘session的方式把所有同一session的请求都发送到相同的Tomcat节点。这样不同用户的请求就被平均分配到集群中各个tomcat节点上,实现负载均衡的能力。这样做的缺点是没有灾难恢复的能力。一旦一个节点发生故障,这个节点上所有的session信息全部丢失;
2、利用Tomcat session复制的机制使得所有session在所有Tomcat节点中保持一致。当一个节点修改一个session数据的时候,该节点会把这个 session的所有内容序列化,然后广播给所有其它节点。这样当下一个用户请求被负载均衡器分配到另外一个节点的时候,那个节点上有完备的 session信息可以用来服务该请求。这种做法的问题是对session哪怕有一点点修改,也要把整个sessions数据全部序列化(serialize),还要广播给集群中所有节点,不管该节点到底需不需要这个session。这样很容易会造成大量的网络通信,导致网络阻塞。一般采用这种方式,当Tomcat节点超过4个时候,整个集群的吞吐量就不能再上升了;
3、第三种方式是通过cookie保存用户信息的一个或几个关键字,每一个http请求到达web应用的时候,web程序拿这个关键字到数据库中读取相关的数据,然后对其进行处理。也就是说把session数据保存到了数据库中。这样以来在内存中的session就完全不需要了。这样做的缺点就是加大了数据库的负载,使得数据库变成了集群的瓶颈。而通过构造数据库集群提高负载能力往往需要高额的成本。

Terracotta的基本原理是对于集群间共享的数据,当在一个节点发生变化的时候,Terracotta只把变化的部分发送给Terracotta服务器,然后由服务器把它转发给真正需要这个数据的节点。这样对网络的压力就非常小,各个节点也不必浪费CPU时间和内存进行大量的序列化操作。把这种集群间数据共享的机制应用在session同步上,相当于对tomcat第二种集群实现机制进行了优化,既避免了对数据库的依赖,又能达到负载均衡和灾难恢复的效果。在对比测试中,采用Terracotta搭建Tomcat集群,节点达到8个时候,整个集群的吞吐量还一直是线性增长的。

Terracotta是一种分布式java集群技术,它巧妙得隐藏了多个分布式JVM带来的复杂性,使得java对象能够透明得在多个JVM集群中进行分享和同步,并能够进行持久化。从某种意义上讲它类似于hadoop中的zookeeper,可以作为zookeeper之外的另外一种选择。
Terracotta 采用的是一种被称之为中心辐射的架构。在这种架构里运行着分布式应用程序的JVM们在启动时都会与一台中心Terracotta服务器相连。 Terracotta服务器负责存储DSO对象数据,协调JVM之间的并发线程。Terracotta库位于应用程序JVM中,在类加载过程中,它们用来对类的字节码进行增强,处理同步块内的lock和unlock请求,处理应用JVM之间的wait(),notify()请求,处理运行时和 Terracotta服务器的联系等等

为了方便使用Terracotta搭建Tomcat集群,Terracotta提供了专门的插件tim-tomcat。

JNDI集群
I. 共享全局JNDI树
Weblogic和JBOSS都使用一个全局的、共享的、分布在整个集群系统的JNDI树,对象被绑定到全局上下文,使用ip多播方式拷贝JNDI数据
集群中的每个节点都有自己的命名服务器,并且自动保存其他所有节点的JNDI数据,因此这种结构具有高度可靠性
实际中,集群JNDI树主要有两个用途:一是用于部署,你只需要将某个EJB部署到一台服务器上,系统会自动将其拷贝到其他节点。二是在运行中你可以用JNDI存取自己的对象,这些自定义对象同样会被自动拷贝到其他节点。
II.独立的JNDI树
Sun JES, IBM Websphere和其他厂商使用的是独立的JNDI树,各个节点拥有自己独立的JNDI,而不会关心其他节点的JNDI。但这并不意味着它们不能实现集群,关键是每台服务器上的配置要相同,应用也要相同,这样的话通过代理agent就能实现高性能的集群了
III. 中央型JNDI树
少数厂商采用中央型的JNDI,所有节点都去一个某个指定的JNDI服务器上获取资源,当然这对于客户端来说是透明的。不过这种方式会大大增加安装和管理的复杂性,因此被大部分厂商放弃

连接JNDI集群
使用JNDI,你必须知道提供JNDI服务的域名或IP以及端口,在全局和独立型 JNDI树中,都存在着多个JNDI服务器,客户端要连接哪一个呢,负载均衡和失败恢复又是如何实现的呢?
技术上来说,可以在客户端和 JNDI服务器之间放一个硬件或软件实现的负载均衡器,来实现上述要求,不过大部分厂商都有更加简单的方法:
* SUN JES 和JBOSS 能够令“java.naming.provider.url”这一JNDI属性支持用逗号分隔多个地址,例如,“java.naming.provider.url=server1:1100,server2:1100,server3:1100,server4:1100”,客户端会逐个搜索,直到找到一个可用的为止
* JBOSS还支持自动找寻功能,即令“java.naming.provider.url”属性为空,客户端会自动以网络多播的方式寻找一个JNDI服务器作为查询的起始点

集群中的单例
1 对于无状态的功能性组件,无需共享状态,所以单例没问题,这个时候我们可以称之为“容器管理的单例”。
2 对于并发访问的业务对象,我们就需要进行DDD,或者类似概念建模,通过聚合来控制访问,此时的单例我们可以称之为“缓存管理的单例”。
如果单例中有状态(例如:有个属性flag)。最简单的办法是把此属性放入数据库。或者分布式缓存

WebLogic 集群级别单例服务
WebLogic Server已经在多个版本中支持单例服务模式,它可以使服务器维护一个集群级别的配置好的单例服务,在其中的一个受管服务器中产生一个单实例对。这个单例可以迁移到集群中的另一台服务器,可以是自动迁移,例如当前的服务器发生故障,或者通过管理员的操作手动进行。 单例可以配置一个偏好服务器来运行,同时可选择一批可以迁移到的多台服务器名称。该单例可部署为独立的服务,或者作为部署的应用程序的一部分。有了这个功能之后,很多人经常会问,这个服务能不能在集群中创建一个单例服务,然后别的应用可以去调用它呢? 答案是肯定的,不过这需要我们想一种办法,将这个服务以可远程访问的方式暴露到集群中,这样它就可以通过查找的方式进行调用了。 因为单例只是一个普通的Java类,只需实现接口: weblogic.cluster.singleton.SingletonService ,然后WebLogic服务器就对这个对象进行实例化和管理,这个对象本身并未提供任何对集群中应用可见的调用接口。 如想允许其它应用使用此单例服务,那么在实现基本的单例逻辑功能代码之外,还需要执行下列任务:

向单例服务加入RMI Remote 接口,这样就可使其被集群中的任何服务器访问和调用
单例服务被激活(activated)时,将其自身绑定到集群范围的JNDI树上
单例服务被停用(deactivated)时,将其自身从集群范围的JNDI树上删除

JBoss HASingleton Servic
在jboss集群时,有一种服务被称为高可用单例服务,意思是指在集群中同时肯定有且只会有一个节点提供这个服务。换句话说这种服务只会在一个节点上启动,并且当这个节点不可用时,集群中会有另一个节点代替它,它是一个具有故障恢复能力的单例服务。要实现这种服务,最简单的方法就是把部署单元,如war, ear, jar,等放到jboss/server/all/deploy-hasingleton/ 目录下。但是放在这个目录下的应用是不能热部署,也不具有farm deploy功能的。还好jboss里有一个很特殊的mbean叫 jboss.ha:service=HASingletonDeployer,type=Barrier,利用它可以很容易的实现高可用单例服务。这个 bean很特殊,只有在集群的master节点上它才会被启动,而一旦master节点不再是master,它又会停止。所以一个普通的部署单元,只要依赖上了barrier,就会具有高可用单例服务的能力。下面举一个例子。

现在要实现一个高可用的消息驱动bean,监听远程机器上的消息topic/a。如果只在集群中的一个节点上部署这个MDB,一旦这个节点不可用,消息处理就停止了。为了提高可用性,可以在每个节点上部署这个 MDB,但这时同一个消息会被每个MDB收到和处理,这不是我们想要的,因为同一个消息应该只被一个 MDB处理。这个时候只要让这个MDB对barrier依赖,再把它部署在所有节点上,那么就只有主节点上的MDB会运行,并且当主节点完蛋时,其他节点变成主节点时,那个节点上的MDB会自动运行。这样我们就实现了高可用单例消息驱动bean服务。

要让mbean对barrier 依赖,只要在它的部署文件里填上一行,例如:
<server>
    <mbean name="xx" code="xx.xx.xxx">
        <depends>jboss.ha:service=HASingletonDeployer,type=Barrier</depends>
    </mbean>
</server>

对于war,jar,ear,可以在jboss.xml里添加,具体见jboss文档。对于ejb3,加上下面的annotation就好了:
@org.jboss.annotation.ejb.Depends("jboss.ha:service=HASingletonDeployer,

WebSphere partition facility

集群后synchronized失效是必然的,类似的同步操作必须转换为通过全局的同步互斥机制,例如数据库锁,这样会造成性能的急剧下降,如果系统有大量sychronized的处理不适合做集群。

原创粉丝点击