Java for Web学习笔记(八六):消息和集群(1)一般性了解
应用消息:紧耦合和松耦合
应用消息常用的是RPC和发布/订阅,其实就是分布代表了同步和异步。随着大系统的发展,微服务架构的兴起,松耦合的发布订阅模式越来越流行。
以员工系统为例,涉及财务报税服务,报销服务,工资单服务,福利服务等等,新的服务可能不断出现。如果采用紧耦合的RPC,则员工系统必须对服务了解,并主动调用这些服务,每新增一个服务,员工系统都要进行update。随着新服务的增加,员工系统就越来越复杂,业务逻辑越来越庞大,代码的维护、测试愈困难,任何一个服务出现问题,都会影响员工系统的运作。
社交网络则采用发布订阅方式。发布者无需向每个订阅者逐个发布信息,甚至不关心有没有订阅者。这种解耦的系统会更为简单和易于维护和测试。
发布订阅模式
发布订阅模式有三个角色:发布者(publisher)、订阅者(subscriber)和代理(broker)。以微博为例,微博就是broker,用户即是publisher也是subcriber。broker实现对消息的分派,其本质就是对消息队列的管理。有不少优秀的开源broker项目。后面将学习使用AMQP(Advanced Message Queuing Protocol)的实现RabbitMQ server作为broker。
集群
集群是个很大的topic。
支持分布:<distributable />
一个J2EE应用被认为是分布式的,要同时满足下面的情况:
- web.xml中带有<distributable />
- 引用的jar包,如果包含了web-fragment.xml,则也必须带有<distributable />;如果没有web-fragment.xml,这个jar包不属于web片段,也就不管是否支持分布式了。
我们看一下log4j-web的jar包:
在JVM之间共享HttpSession对象对分布式的集群是重要的。<distributable />表示应用将用于部署在多个JVM上,要求HttpSession的attributes是Serializable,也即HttpSession可以在不同的JVM之间传递。如果我们设置了<distributable />,而在HttpSession中加入了一个非序列化的属性,则tomcat会抛出IllegalArgumentException异常。
序列化
Java对象通过序列化,可以在集群之间传递。关于序列化,我们在后面有专门的学习。HttpSession是否能够序列化,可以通过javax.servlet.http.HttpSessionActivationListener来监听。@WebListener
public class TempSessionListerner implements HttpSessionActivationListener {
public TempSessionListerner() {
// TODO Auto-generated constructor stub
}
/** 表示不适合序列化,只能从其他JVM中接收。对于不合适序列化的参数,标识为transient(不参加序列化),例如文件描述符,数据库连接等
* 在此处接收后,重新创建或者获取本地对象。 */
public void sessionDidActivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
/** 表示适合序列化,可以传递到其他JVM。 */
public void sessionWillPassivate(HttpSessionEvent se) {
// TODO Auto-generated method stub
}
}
在不同的应用之间共享HttpSession,导致每次HttpSession发生变化时,都必须序列化,重新共享,这很可能会引发性能问题。因此,对于集群,倾向于session stickiness(会话粘滞),也就是同一个session,发给同一app处理,HttpSession的共享只是周期性进行。但是如果配置session stickiness,不同的container或者load balance有不同的配置方式,而周期长度的设置取决于开放团队的经验。但是,这也是有漏洞的,如果server down掉了,就可能出现session状态不同步。
设置Container
不同容器配置不一样,如果使用GlassFish Domain Administration Server (DAS),那我们就只需要设置<distributable/>。然而tomcat配置就复杂很多,其缺省配置不支持集群。除了多个容器组成集群外,我们可以加上load banlance,常采用下图的两种组网方式。左图在传统的企业网常见,Apache HTTPD运行Apache Tomcat Connector,可以将请求路由到负载最轻的Tomcat server。但不支持WebSocket。右图将智能负载均衡有load balance来进行,虽然不能像Apache Tomcat Connector了解那么详细的节点性能,但是优秀的负载均衡器可仍可以利用简单的定期health-check请求了检查server。
将Tomcat server加入集群,最简单的方式是在server.xml中的Engine和Host配置中增加:
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
详细还是要去看看tomcat集群设置的资料。
消息在集群中传递
几个使用场景例子:
- 在大的集群中通过消息传递update的内容。
- 跨机器的消息传递
- 对于后台运行的任务,通过消息可以传递其运行状态,只要访问集群中任一个server都可以获得当前状态。
我们需要注意到,消息在集群中传递是异步的,且时间不同,甚至差别较大。
Java Message Service 2.0 (JSR 343)
Java Message Serivce是工业级的消息服务,最早在2001年引入,在2013年发布了JMS 2.0。JMS是API,不是协议,因此虽然代码移植很方便,但是互联互通有问题,而且限于Java语言。
Advanced Message Queuing Protocol
AMQP不是API,是协议,定义了网络传输中packet的结构。其官网是这样定义的[1]:The Advanced Message Queuing Protocol (AMQP) is an open standard for passing business messages between applications or organizations. It connects systems, feeds business processes with the information they need and reliably transmits onward the instructions that achieve their goals.
AMQP最早在2003年,与2013年发布1.0,目前被广泛地应用。Spring framework没有内置AMQP,但是Spring有AMQP项目,可以帮助我们在spring framework中使用AMQ,但我们后面的例子将直接使用AMQP的 Java Client library。