Archive for the ‘经验’ category

异步调用

June 29th, 2010

1 什么是异步

异步显然是和同步相对应的。同步调用,被调用者要执行完所有的代码才将执行流程交给调用者,在被调用者执行的过程中,调用者只能被动的等待;而异步,则是被调用先将执行流程转给调用者,然后自己再继续执行。如果调用者必须在得到被调用者的所有执行结果才能确定下一步该做什么的话,那么这种情况就该使用同步调用;相反,如果调用者不需要被调用者的执行结果,那么就可以使用异步。

2 怎样异步

在单线程或单进程等单一执行流程的情况下,实现不了能让被调用者个调用者并发执行的异步调用,这种情况下被调用者只能先将要执行的任务环境记录下来,带调用者执行完成后,再回头来执行之前要执行的任务,这样的方式其实算不上是异步,称之为推迟执行更贴切。

在多线程或多进程环境里,才能真正的实现异步,实现调用者和被调用者并发执行的异步调用。被调用者只要开启一个新的线程在新线程里执行任务,而立刻返回让调用者在当前线程继续执行,这样就可以实现异步调用了。

借助于ejb 3.1 , spring 3, seam这样的容器和框架,实现异步调用的方法非常简单,只需要再方法上加上指定的Annotation就可以了。使用spring 2的时候,可以注入taskExecutor,用taskExecutor.execute(Runnable r) 方法来实现异步。另外,通过JMS也可以实现异步。

3 异步有什么好处

说异步比同步调用性能好并不准确。假设 被调用方法里面要完成两个任务,分别耗时 t1和t2,从开始调用到调用返回的耗时,即响应时间,同步调用的时候,Rs = t1 + t2,异步调用的时候Ra = t1 + a1,其中a1代表为了异步调用开启新线程或新进程的耗时,如果要Ra < Rs,显然要求a1 < t2,可见如果异步执行的任务并不比启用异步执行耗时太多的话,异步执行也不会缩短响应时间。如果计算完成同等任务的总耗时的话,同步的时候 Ts = t1 + t2,异步的时候 Ta = t1 + a1 + t2,可见Ts < Ta,此时异步没有任何好处。在要求响应时间非常短的互联网应用中,合理的采用异步可以提高响应速度,提升程序的负载能力。快速的响应用户的请求,将耗时多而用户不急于知道结果的任务交给有限的线程来执行。

然而,对于Java EE这样的环境,一个事务的边界往往是在一个线程里的,所以使用异步之后,就失去了事务的保护,异常后的数据补偿工作增加了程序的复杂性,也让程序更容易出错。一个不好的异步实现机制,往往给程序带来更多的隐患。比如,为每一个异步执行的任务都新建一个线程,很可能在并发数到一定程度的时候,由于创建过多的线程,达到性能瓶颈,甚至于导致程序崩溃。

4 什么时候用异步

首先只有调用者不需要立刻知道结果的任务才能被异步执行;其次被异步执行的任务耗时多于使用异步执行本身的时候,采用异步才值得;再次,要求响应时间短的时候采用异步才有意义。

典型的例子:用户支付订单后要立刻告知用户支付是否成功,并给用户发送邮件,通知仓库发货,告知物流送货。这种情况下,用户只要知道支付成功就行了,发送邮件等任务用户可以过会再感知到,而且发送邮件等任务很耗时,用户肯定不愿意等待完成所有一切之后才知道支付结果。这种情况下,将发送邮件等任务异步执行就很合适。

另外一个例子,A,B两个系统每隔一个小时对一次账,如果B发现账户和A系统不一样,则自动调整。尽管A不需要知道B的调整结果,但是A完全可以等待B执行,反正也没人等也没人急,而且对账频率低负载低,这种情况下就没有必要异步执行调整动作。

  • Share/Bookmark

招聘程序员感受

April 13th, 2010

这已经不是第一次招聘程序员了,每一次都很痛苦。以前只知道找工作不容易,现在发现招工也不容易。招聘就像找对象一样,明明满世界的男人女人,就是找不到你合适的人。

合适的人不是牛人,对于我们这样一个不算太有钱的普通电子商务私营企业,不需要那么多的牛人,也养不起那么多的牛人。除非有非常牛的领导,否则一堆牛人在一起可能只会做出烂事。而且牛人们要做牛的项目,否则没有满足感,会离职的。我就遇到过一个毕业生,觉得自己牛,要做“高并发,高访问量”的程序,才来几天就走了。所以如果你被招聘方拒绝了,也许是因为你太优秀了。

招聘的效率太低了。每次发出招聘启事,都可以收到大量的,不对,是巨量的简历。尽管人力资源部的同事们已经依据其它条件过滤掉了大量不合格简历,仍有数十倍于目标招聘人数的简历到我这里来。经过精心过滤后,去掉近四分之三的简历后,剩下四分之一的要面试的人还是很多。假如要招聘2个人,人力资源部的人过滤后剩100人,再过滤后剩20人面试,只有4人能通过,2人薪资谈不拢,只有2人留下,一年后可能有一人离职。招聘就是这样低效率。

因此,我很希望能招聘到能安心工作的程序员。如果一个程序员的简历里,工作时间不长,却去过很多公司,每家都工作时间不长,那我一定不要他;相反,如果有人能在一家公司里安心做过很多年,那我一定要给他个面试机会

面对众多的简历,我到底要面试那些人呢?我一般是按以下步骤筛选的:

  1. 先去掉不满足硬性条件的,比如薪资要求过高,学历过低,工作经验太少。
  2. 去掉一看就觉得不想要的,比如跳槽频繁的,经历过于复杂的,一看就狗屁不通的。
  3. 挑出一看就喜欢的,比如项目经历很符合我的要求,简历中有闪光点的。
  4. 去掉平庸的简历。
  5. 最后从剩下的简历中选出几个较好的凑数。



所以,为了以后简历好点,要慎重对待现在的工作,不要给以后的简历留下污点。然后努力在自己平日的工作中寻找闪光点,计入将来的简历。如果没有污点,也没有闪光点,那就要让自己的简历不平庸。什么样的简历是平庸的?冗长而空洞,短小而无物。简单说,简历的目的是要让招聘者看了后想应聘你,而不能说明为什么人家要录用你的简历,不论多长多短多夸张多好看,都是空洞无物的。如何让招聘者想录用你?首先,要体现你渴望得到这份工作;其次,要说明你如何能胜任这份工作。要做到这两点,就要重视每一次应聘,最好对你要应聘的公司做事前的了解,为要应聘的职位定制简历。一份简历,巨量投放,随叫随到,即时发挥,成功率肯定很低,失败多次后就没有底气,越来越不行。我看了大量的简历,把以前做的项目的模块功能罗列了一番,对自己在项目做过什么,得到什么,有什么想法,寥寥数语,甚至只字不提,这样的简历如何能让我了解你做过什么,能做什么,想做什么?我又如何敢录用你?

我真希望每位应聘者都能认真对待自己的工作和简历,那样无论是应聘者还是招聘者都会方便很多。

  • Share/Bookmark

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