Archive for the ‘经验’ category

JAXB中如何利用继承生成XML

November 14th, 2009

问题

JAXB是我用过的java处理XML的方法中做方便的一个,在jaxb中如何使用类的集成关系有一个小小的需要注意的地方。看下面的两端XML。
XML 示例1:

<a>
    <b></b>
    <c></c>
<a>

XML示例2:

<a>
    <b></b>
    <d></d>
<a>

这两段XML的唯一差别就在c元素与d元素。为了生成这两个XML,有几种方案。

方案一

不利用类的继承来重用代码,写两套Java代码, 如:

@XmlRootElement(name = a)
public class A {
    B b;
    C c:
}
 
@XmlRootElement(name = a)
public class A2 {
    B b;
    D d:
}
 
public class B {}
public class D {}

这一方案不是我们想要的,虽然可以解决问题,但是不能重用两个XML中共有的结构。

方案二

利用一个父类表示两个XML之间的共同的结构,用两个子类扩展这个父类,分别添加 C 和 D 元素。
代码如下:

@XmlRootElement(name = "a")
public class A {
    B b;
}
 
public class B {}
public class C {}
public class D {}
 
 
public class E extends A {
    C c;
}
 
public class F extends A {
    D d;
}

这一方案看似很直观,但是实践中却发现 E 和 F 却都只能生成 父类的 部分,如下的XML:

<a>
    <b></b>
<a>

即使把 @XmlRootElement(name = “a”) 移到 E 和 F类上也行不通。

方案三

用一个类代表共同的结构,用一个父类代表C和D元素,在用两个类代表具体化的C和D类,如:

@XmlRootEelemnt(name = "a")
public class A {
    B b;
 
    @XmlElements({
        @XmlElement(name = "c", type=C.class),
        @XmlElement(name = "d", type=D.class)
    })
    E e;
}
public class B {}
public class E {}
 
public class C extends E {}
public class D extends E {}

这样做才最终达到目的。关键点就是@XmlElements的使用。

  • Share/Bookmark

web服务器集群

August 3rd, 2009

什么是集群

计算机集群(以下简称集群)在维基百科上被定义为一组相互连接的计算机,紧密的工作在一起,以至于在很多方面看来,它们都像是一台机器。

集群的好处

集群可能能给我们带来很多好处,其中负载均衡(loadbalance)和故障恢复(failover)一般是最常用的。负载均衡是将系统的负载分派到集群内不同的计算机上,让每个节点都不至于太忙或太闲,通过增加集群中计算机的数量,可以提升整体的负载能力。故障恢复是指在集群中某个节点发生故障时,其它的节点代替它们继续工作,这样整个系统依旧能对外提供服务。

web服务器集群的特点

对于web服务器来说,集群就是让一组计算机对外像一台web服务器一样运行。web服务器的特点是它通过http协议与客户端交互,而http协议的工作机制是请求响应模式的。web服务器的集群要能把客户端的http请求发送到集群内各个节点上,实现loadbalance,并能探测到各节点的工作情况,在某个节点失效的时候,不再把http请求发送到该节点上。

有状态与无状态

web程序本身是否有状态对实现集群的方法和难以度有很大的影响。如果web程序和客户端交互的记录下了一些状态信息,并在处理之后的请求时需要知道这样的信息,那么这个程序就是有状态的。比如,登录功能,当用户正确登录后,web程序会记录下用户的登录状态,当用户访问其它的页面时,web程序会参照用户的登录信息做不同的处理。在实现集群的时候,有状态的程序要复杂些,因为必须保证处理请求的节点上能有该用户的状态信息。对于无状态的程序,则没有这个问题,请求被分发到任何节点都是一样的,没有额外的维护状态的工作。

为了保证处理请求的节点上能有状态信息,要么在多个节点上复制程序的状态,要么采用否种策略保证将请求正确的发送到具有相应状态的节点上。如上面的例子,要么把用户登录的信息复制到集群的所有节点上,以保证无论之后请求发送到哪个节点上,都能够找得到登录信息,要么每次都把这个用户的请求发送到同一个节点上,就像mod_jk的sticky_session那样。

多线程与多进程

php,ruby,python等脚本语言往往采用无状态的结构,而java往往采用有状态的结构。并非语言决定了它们的工作方式,更多的是一种习惯。

java web服务在运行时是一个常驻内存的进程,用不同的线程处理用户请求。当处理一个http请求的线程结束后,保留在该进程内的信息并没有消失,因为进程还在。因此,可以很方便的把一些交互信息放到java进程的内存中,在多个线程间共享。在加上又有好用的HttpSession类,这样在java ee里把用户信息保存到session中,是一件非常容易的事,程序员们也自然的就采用的,导致了java web程序成了有状态的。如果完全不用session等,java web程序也能做到无状态。

和java的单进程,多线程工作方式不同,php等脚本语言采用了多进程的工作方式。当有一个http请求到来时,web服务器开启一个新的进程,这个进程调用脚本语言的解释器来运行脚本处理请求,处理结束后进程退出。如果要在多个进程间共享信息,就得采用外部的介质,DB就成了最好的选择。

本质上来说,只要需要记录会话的状态,就肯定有状态,差别只是把状态放到什么地方。java ee里往往把它放到应用程序的内存里,而php等则往往放到db里。

  • Share/Bookmark

TemplateServlet的局限性

July 10th, 2009

我想利用TemplateServlet,像使用php那样使用groovy。只要设置好了,就可以在web目录下写gsp文件,就像写php那样,即时写即时看到效果。

但是我错了,目前的TemplateServlet非常简陋,远不能达到要求。问题主要发生在include功能。php的inclue require等功能,可以把页面变成方法库,或类库,但是在TemplateServlet的gsp里不行。我无法访问被包含的页面里的方法,闭包等。想想也很自然,每个gsp文件都有自己的名称空间。

另外,不能在gsp里定义类,这个太伤了~~~

如果能解决以上两个问题,TemplateServlet就可以让groovy像php那样使用了。唉,那些做grails的家伙真的应该先把这块搞好,再搞重量级的东西,毕竟不是所有人都想用spring hibernate的。

看了看gails的roadmap,今年9月的1.2版本里将有standalone gsp module,那是真正的gsp,而不是现在的简单的TemplateServlet。Grails里的gsp支持页面片段和非常方便的自定义标签,前者便于共享页面展示,后者便于共享页面行为,那么处理页面的是什么呢?还要写controller吗?到时候看吧,还是很令人期待的。

  • Share/Bookmark

IO和性能

July 3rd, 2009

最近几天看了几家互联网公司的和架构有关的PPT,有支付宝,linkedin,douban等。这些公司所使用的具体的技术有差别,但是为了能做到支持大规模访问,他们都在与缓慢的IO做斗争! 比如说豆瓣网的历次架构变迁,不是因为磁盘IO成为瓶颈,就是因为网络IO成为瓶颈。如果能把IO的问题解决掉,那么对于互联网企业来说,性能问题就解决掉一半了,我想。

与飞速的CPU和内存相比,磁盘和网络的IO慢如蜗牛,这是问题的根源。如果没有大规模廉价而又足够快的存储方案出现的话,这个问题还将继续下去。我们只能期待这一革命性的技术出现来从根本上解决这一问题吧!

那对于现在的我们该怎么办呢?大家的想法可能都是一样的,一是加快IO速度,二是避免不必要的IO。豆瓣曾经通过购买更高性能的硬盘来解决这个问题,如果想要更好的效果,需要投入更多的资金来购买整套的高性能存储方案,但是这成本太高了,很多公司后来都会放弃。于是大部分公司都会极力采用后者,就是避免不必要的IO,缓存就是用来做这样的事的。

将低速介质上的数据放到高速介质上,访问者先访问高速介质,在找不到需要数据时才访问低速介质,以此来提升访问数据的速度,这样的技术就是缓存了。设在一段时间内有m次没有命中缓存,n次命中,而访问一次缓存的时间是t1, 访问慢速介质的时间是t2,那么为了是缓存有效就得满足不等式: m * (t1 + t2) + n * t1 < (m + n) * t2, 于是有 m / n < t2 / t1 -1 。由此可见,在介质的速度比一定的情况下,命中比率要高于一定的值,缓存才有效果。提高命中比率,就是要把那些最常用的数据放到缓存里。另一方面,缓存里数据如果被修改了,就要同步到低速存储介质中,同样的,如果低速存储介质的数据被修改了,也要同步到缓存里。如果加上同步的开销,就需要更高的命中率。提高命中率和保证数据的同步,是使用缓存的两个艰巨的任务。

豆瓣网使用memcache,还使用专门的nigix服务器来提供图片的服务,其实也是cache。linkedin则使用了数十台大内存的服务器作为缓存服务器,运行着用c++写的专门的缓存程序。可见这些网站在使用缓存方面都是不遗余力呀。如果能把缓存用好,那么就把一大半的IO问题解决掉了。

IO往往会成为程序的瓶颈,因此,程序员应当时刻的注意,你写的代码是否在进行IO操作,是否可以减少IO操作,等等。

  • Share/Bookmark