Posts Tagged ‘java’

ActiveMQ使用经验

June 18th, 2009

ActiveMQ是apache的一个开源JMS服务器,不仅具备标准JMS的功能,还有很多额外的功能。公司里引入ActiveMQ后,ActiveMQ成里我们公司业务系统中最重要的一个环节。所有应用都通过jms集成,如果ActiveMQ出了故障,整个系统就瘫痪了。因此,头对ActiveMQ的性能,可靠性,以及如何正确使用,是非常的关心的,而我就被指派来做关于ActiveMQ的调研,本文对此做了些总结。

1 使用jms需要注意的问题

一下所述的问题,不仅是对ActiveMQ,对于其他的JMS也一样有效。

1.1 不要频繁的建立和关闭连接

JMS使用长连接方式,一个程序,只要和JMS服务器保持一个连接就可以了,不要频繁的建立和关闭连接。频繁的建立和关闭连接,对程序的性能影响还是很大的。这一点和jdbc还是不太一样的。

1.2 Connection的start()和stop()方法代价很高

JMS的Connection的start()和stop()方法代价很高,不能经常调用。我们试用的时候,写了个jms的connection pool,每次将connection取出pool时调用start()方法,归还时调用stop()方法,然而后来用jprofiler发现,一般的cpu时间都耗在了这两个方法上。

1.3 start()后才能收消息

Connection的start()方法调用后,才能收到jms消息。如果不调用这个方法,能发出消息,但是一直收不到消息。不知道其它的jms服务器也是这样。

1.4 显示关闭Session

如果忘记了最后关闭Connection或Session对象,都会导致内存泄漏。这个在我测试的时候也发现了。本来以为关闭了Connection,由这个Connection生成的Session也会被自动关闭,结果并非如此,Session并没有关闭,导致内存泄漏。所以一定要显示的关闭Connection和Session。

1.5 对Session做对象池

对Session做对象池,而不是Connection。Session也是昂贵的对象,每次使用都新建和关闭,代价也非常高。而且后来我们发现,原来Connection是线程安全的,而Session不是,所以后来改成了对Session做对象池,而只保留一个Connection。

2 集群

ActiveMQ有强大而灵活的集群功能,但是使用起来还是会有很多陷阱。

2.1 broker cluster和 master-slave

ActiveMQ可以做broker的集群,也可以做master-slave方式的集群。前者能在多个broker之前fail-over和load-balance,但是在某个节点出故障时,可能导致消息丢失;而后者能实时备份消息,和fail-over,但是不能load-balance。broker cluser的方式,在一个broker上发送的消息可以在其它的broker上收到。当一个broker失效时,客户端可以自动的转到别的broker上运行,多个broker可以同时提供服务,但是消息只存储在一个broker上,如果那个broker失效了,那么客户端直到它重新启动后才能收到该broker上的消息,假如很不幸,那个broker的存储介质坏了,那么消息就丢失掉了。
Master-slave方式中,只有master提供服务,slave只是实时的备份master的数据,所以消息不会丢失。当master失效时,slave会自动升为master,客户端会自动转到slave上工作,所以能fail-over。由于只有master提供服务,所以不能将负载分到多个broker上。
其实单个broker的性能已经是相当的惊人了,在我们公司的机器上能达到每秒收发4000个消息,没个消息4K字节这样的速度,足够公司目前的需要了,而公司并不希望丢失任何数据,所以我们选择使用master-slave模式。

2.2 多种master-slave模式

master-slave也有多种实现方式。它们的不同只是在共享数据和锁机制上。

2.2.1 Pure master-slave

Pure master-slave,显示的在配置文件中指定一个broker做为另一个broker的slave。运行时,slave同过网络自动从master出复制数据,同时在和master失去连接时自动升级为master。当master失效,slave成为master后,如果要让原先的master重新投入运行,需要停掉运行中的slave(现在升级为master了),手动复制slave中的数据到master中。再重新启动master和slave。这种方式最简单,效率也不错,但是只能有两台做集群,只能fail-over一次,而且需要停机回复master-slave结构。

2.2.2 JDBC master-slave

这种方式不需要特殊的配置,只要让所有的节点都把数据存储到同一个数据库中。先拿到数据库表的锁的节点成为master,一旦它失效了,其它的节点获得锁,就可以成为master。因为数据通过数据库共享,放在一个地方,不需要停机恢复master-slave。这种方式,需要额外的数据库服务器,如果数据库失效了,那么就全失效了,而且速度不是很快。我们在用mysql测试时,并没有成功,master失效后,其他的节点始终没有升级成slave,可能是数据库配置的问题。

2.2.3 Share file master-slave

这种方式类似于前者,也不需要特别的配置,只是通过共享文件系统来共享数据,靠文件锁实现只有一台成为master。共享文件系统的方式有很多,我们测试了nfs v4 (v3有bug,不行), 最终在稳定性,效率等方面不是很满意,可能是通过网络太慢了。

测试过众多master-slave模式后发现,pure方式管理起来麻烦,jdbc方式成本高,效率低,share file方式需要高性能的共享文件,都有缺点。鉴于单台activeMQ很可靠,而我们的基础平台组愿意用硬件备份,最终还是决定不用master-slave了,也不用broker cluster,就用单台,通过硬件冗余保证数据不会丢失,并找另外一台刀片机做冷备,在主服务器失效时顶替。

  • Share/Bookmark

我为公司制定的Java代码规范

June 5th, 2009

Java代码规范

本Java代码规范以SUN的标准Java代码规范为基础,为适应我们公司的实际需要,可能会做一些修改。本文档中没有说明的地方,请参看SUN Java标准代码规范。如果两边有冲突,以本文档为准。

1. 标识符命名规范

1.1 概述

标识符的命名力求做到统一、达意和简洁。

1.1.1 统一

统一是指,对于同一个概念,在程序中用同一种表示方法,比如对于供应商,既可以用supplier,也可以用provider,但是我们只能选定一个使用,至少在一个Java项目中保持统一。统一是作为重要的,如果对同一概念有不同的表示方法,会使代码混乱难以理解。即使不能取得好的名称,但是只要统一,阅读起来也不会太困难,因为阅读者只要理解一次。

1.1.2 达意

达意是指,标识符能准确的表达出它所代表的意义,比如: newSupplier, OrderPaymentGatewayService等;而 supplier1, service2,idtts等则不是好的命名方式。准确有两成含义,一是正确,而是丰富。如果给一个代表供应商的变量起名是 order,显然没有正确表达。同样的,supplier1, 远没有targetSupplier意义丰富。

1.1.3 简洁

简洁是指,在统一和达意的前提下,用尽量少的标识符。如果不能达意,宁愿不要简洁。比如:theOrderNameOfTheTargetSupplierWhichIsTransfered 太长, transferedTargetSupplierOrderName则较好,但是transTgtSplOrdNm就不好了。省略元音的缩写方式不要使用,我们的英语往往还没有好到看得懂奇怪的缩写。

1.1.4 骆驼法则

Java中,除了包名,静态常量等特殊情况,大部分情况下标识符使用骆驼法则,即单词之间不使用特殊符号分割,而是通过首字母大写来分割。比如: SupplierName, addNewContract,而不是 supplier_name, add_new_contract。

1.1.5 英文 vs 拼音

尽量使用通俗易懂的英文单词,如果不会可以向队友求助,实在不行则使用汉语拼音,避免拼音与英文混用。比如表示归档,用archive比较好, 用pigeonhole则不好,用guiDang尚可接受。

1.2 包名

使用小写字母如

com.xxx.settlment

,不要

com.xxx.Settlement

单词间不要用字符隔开,比如

com.xxx.settlment.jsfutil

,而不要

com.xxx.settlement.jsf_util

1.3 类名

1.3.1 首字母大写

类名要首字母大写,比如 SupplierService, PaymentOrderAction;不要 supplierService, paymentOrderAction.

1.3.2 后缀

类名往往用不同的后缀表达额外的意思,如下表:

后缀名 意义 举例
Service 表明这个类是个服务类,里面包含了给其他类提同业务服务的方法 PaymentOrderService
Impl 这个类是一个实现类,而不是接口 PaymentOrderServiceImpl
Inter 这个类是一个接口 LifeCycleInter
Dao 这个类封装了数据访问方法 PaymentOrderDao
Action 直接处理页面请求,管理页面逻辑了类 UpdateOrderListAction
Listener 响应某种事件的类 PaymentSuccessListener
Event 这个类代表了某种事件 PaymentSuccessEvent
Servlet 一个Servlet PaymentCallbackServlet
Factory 生成某种对象工厂的类 PaymentOrderFactory
Adapter 用来连接某种以前不被支持的对象的类 DatabaseLogAdapter
Job 某种按时间运行的任务 PaymentOrderCancelJob
Wrapper 这是一个包装类,为了给某个类提供没有的能力 SelectableOrderListWrapper
Bean 这是一个POJO MenuStateBean

1.4 方法名

首字母小写,如 addOrder() 不要 AddOrder()
动词在前,如 addOrder(),不要orderAdd()
动词前缀往往表达特定的含义,如下表:

前缀名 意义 举例
create 创建 createOrder()
delete 删除 deleteOrder()
add 创建,暗示新创建的对象属于某个集合 addPaidOrder()
remove 删除 removeOrder()
init或则initialize 初始化,暗示会做些诸如获取资源等特殊动作 initializeObjectPool
destroy 销毁,暗示会做些诸如释放资源的特殊动作 destroyObjectPool
open 打开 openConnection()
close 关闭 closeConnection()<
read 读取 readUserName()
write 写入 writeUserName()
get 获得 getName()
set 设置 setName()
prepare 准备 prepareOrderList()
copy 复制 copyCustomerList()
modity 修改 modifyActualTotalAmount()
calculate 数值计算 calculateCommission()
do 执行某个过程或流程 doOrderCancelJob()
dispatch 判断程序流程转向 dispatchUserRequest()
start 开始 startOrderProcessing()
stop 结束 stopOrderProcessing()
send 发送某个消息或事件 sendOrderPaidMessage()
receive 接受消息或时间 receiveOrderPaidMessgae()
respond 响应用户动作 responseOrderListItemClicked()
find 查找对象 findNewSupplier()
update 更新对象 updateCommission()

find方法在业务层尽量表达业务含义,比如 findUnsettledOrders(),查询未结算订单,而不要findOrdersByStatus()。 数据访问层,find,update等方法可以表达要执行的sql,比如findByStatusAndSupplierIdOrderByName(Status.PAID, 345)

1.5 域(field)名

1.5.1 静态常量

全大写用下划线分割,如

public static find String ORDER_PAID_EVENT = "ORDER_PAID_EVENT";

1.5.2 枚举

全大写,用下划线分割,如

public enum Events {
    ORDER_PAID,
    ORDER_CREATED
}

1.5.3 其他

首字母小写,骆驼法则,如:

public String orderName;

1.6 局部变量名

参数和局部变量名首字母小写,骆驼法则。尽量不要和域冲突,尽量表达这个变量在方法中的意义。

2. 代码格式

用空格字符缩进源代码,不要用tab,每个缩进4个空格。

2.1 源文件编码

源文件使用utf-8编码,结尾用unix n 分格。

2.2 行宽

行宽度不要超过130。

2.3 包的导入

删除不用的导入,尽量不要使用整个包的导入。在eclipse下经常使用快捷键 ctrl+shift+o 修正导入。

2.4 类格式

2.5 域格式

每行只能声明一个域。
域的声明用空行隔开。

2.5 方法格式

2.6 代码块格式

2.6.1 缩进风格

大括号的开始在代码块开始的行尾,闭合在和代码块同一缩进的行首,例如:

package com.test;

public class TestStyle extends SomeClass implements AppleInter, BananaInter {
    public static final String THIS_IS_CONST = "CONST VALUE";
   
    private static void main(String[] args) {
        int localVariable = 0;
    }
   
    public void compute(String arg) {
        if (arg.length() >  0) {
            System.out.println(arg);
        }
       
        for (int i = 0; i < 10; i++) {
            System.out.println(arg);
        }
       
        while (condition) {
           
        }
       
        do {
            otherMethod();
        } while (condition);
       
        switch (i) {
        case 0:
            callFunction();
            break;
        case 1:
            callFunctionb();
            break;
        default:
            break;
        }
    }
}

2.6.2 空格的使用

2.6.2.1 表示分割时用一个空格

不能这样:

if       (               a >        b   )            {
    //do something here
};
2.6.2.2 二元三元运算符两边用一个空格隔开

如下:

a + b = c;
b - d = e;
return a == b ? 1 : 0;

不能如下:

a+b=c;
b-d=e;
return a==b?1:0;
2.6.2.3 逗号语句后如不还行,紧跟一个空格

如下:

call(a, b, c);

不能如下:

call(a,b,c);

2.6.3 空行的使用

空行可以表达代码在语义上的分割,注释的作用范围,等等。将类似操作,或一组操作放在一起不用空行隔开,而用空行隔开不同组的代码, 如图:

order = orderDao.findOrderById(id);

//update properties
order.setUserName(userName);
order.setPrice(456);
order.setStatus(PAID);

orderService.updateTotalAmount(order);


session.saveOrUpdate(order);

上例中的空行,使注释的作用域很明显.

  • 连续两行的空行代表更大的语义分割。
  • 方法之间用空行分割
  • 域之间用空行分割
  • 超过十行的代码如果还不用空行分割,就会增加阅读困难

3. 注释规范

3.1 注释 vs 代码

  • 注释宜少二精,不宜多而滥,更不能误导
  • 命名达意,结构清晰, 类和方法等责任明确,往往不需要,或者只需要很少注释,就可以让人读懂;相反,代码混乱,再多的注释都不能弥补。所以,应当先在代码本身下功夫。
  • 不能正确表达代码意义的注释,只会损害代码的可读性。
  • 过于详细的注释,对显而易见的代码添加的注释,罗嗦的注释,还不如不写
  • 注释要和代码同步,过多的注释会成为开发的负担
  • 注释不是用来管理代码版本的,如果有代码不要了,直接删除,svn会有记录的,不要注释掉,否则以后没人知道那段注释掉的代码该不该删除。

3.2 Java Doc

表明类、域和方法等的意义和用法等的注释,要以javadoc的方式来写。Java Doc是个类的使用者来看的,主要介绍 是什么,怎么用等信息。凡是类的使用者需要知道,都要用Java Doc 来写。非Java Doc的注释,往往是个代码的维护者看的,着重告述读者为什么这样写,如何修改,注意什么问题等。 如下:

/**
* This is a class comment
*/

public class TestClass {
    /**
    * This is a field comment
    */

    public String name;

    /**
    * This is a method comment
    */

    public void call() {
   
    }
}

3.3 块级别注释

3.3.1 块级别注释,单行时用 //, 多行时用 /* .. */。

3.3.2 较短的代码块用空行表示注释作用域

3.3.3 较长的代码块要用

/*------ start: ------*/

/*-------- end: -------*/

包围
如:

/*----------start: 订单处理 ------- */
//取得dao
OrderDao dao = Factory.getDao("OrderDao");

/* 查询订单 */
Order order = dao.findById(456);

//更新订单
order.setUserName("uu");
order.setPassword("pass");
order.setPrice("ddd");

orderDao.save(order);
/*----------end: 订单处理 ------- */

3.3.4 可以考虑使用大括号来表示注释范围

使用大括号表示注释作用范围的例子:

/*----------订单处理 ------- */
{
    //取得dao
    OrderDao dao = Factory.getDao("OrderDao");

    /* 查询订单 */
    Order order = dao.findById(456);

    //更新订单
    order.setUserName("uu");
    order.setPassword("pass");
    order.setPrice("ddd");

    orderDao.save(order);
}

3.4 行内注释

行内注释用 // 写在行尾

4 最佳实践和禁忌

4.1 每次保存的时候,都让你的代码是最美的

程序员都是懒惰的,不要想着等我完成了功能,再来优化代码的格式和结构,等真的把功能完成,很少有人会再愿意回头调整代码。

4.2 使用log而不是System.out.println()

log可以设定级别,可以控制输出到哪里,容易区分是在代码的什么地方打印的,而System.out.print则不行。而且,System.out.print的速度很慢。所以,除非是有意的,否则,都要用log。至少在提交到svn之前把System.out.print换成log。

4.3 每个if while for等语句,都不要省略大括号{}

看下面的代码:

if (a > b)
    a++;

如果在以后维护的时候,需要在a > b 时,把b++,一步小心就会写成:

if (a > b)
    a++;
    b++;

这样就错了,因为无论a和b是什么关系,b++都会执行。 如果一开始就这样写:

if (a > b)  {
    a++;
}

相信没有哪个笨蛋会把b++添加错的。而且,这个大括号使作用范围更明显,尤其是后面那行很长要折行时。

4.4 善用TODO:

在代码中加入 //TODO: ,大部分的ide都会帮你提示,让你知道你还有什么事没有做。比如:

if (order.isPaid()) {
    //TODO: 更新订单
}

4.5 在需要留空的地方放一个空语句或注释,告述读者,你是故意的

比如:

if (!exists(order)) {
    ;
}

或:

if (!exists(order)) {
    //nothing to do
}

4.6 不要再对boolean值做true false判断

比如:

if (order.isPaid() == true) {
    // Do something here
}

不如写成:

if (order.isPaid()) {
    //Do something here
}

后者读起来就很是 if order is paid, …. 要比 if order’s isPaid method returns true, … 更容易理解

4.7 减少代码嵌套层次

代码嵌套层次达3层以上时,一般人理解起来都会困难。下面的代码是一个简单的例子:

public void demo(int a, int b, int c) {
    if (a > b) {
        if (b > c) {
            doJobA();
        } else if (b < c) {
            doJobB()
        }
    } else {
        if (b > c) {
            if (a < c) {
                doJobC();
            }
        }
    }
}

减少嵌套的方法有很多:

  • 合并条件
  • 利用 return 以省略后面的else
  • 利用子方法

比如上例,合并条件后成为:

public void demo(int a, int b, int c) {
    if (a > b && b > c) {
        doJobA();
    }
    if (a > b && c > b) {
        doJobB();
    }
    if (a <= b && c < b && a < c) {
        doJobC();
    }
}

如果利用return 则成为:

public void demo(int a, int b, int c) {
    if (a > b) {
        if (b > c) {
            doJobA();
            return;
        }
        doJobB()
        return;
    }

    if (b > c) {
        if (a < c) {
            doJobC();
        }
    }
}

利用子方法,就是将嵌套的程序提取出来放到另外的方法里。

4.8 程序职责单一

关注点分离是软件开发的真理。人类自所以能够完成复杂的工作,就是因为人类能够将工作分解到较小级别的任务上,在做每个任务时关注更少的东西。让程序单元的职责单一,可以使你在编写这段程序时关注更少的东西,从而降低难度,减少出错。

4.9 变量的声明,初始化和被使用尽量放到一起

比方说如下代码:

int orderNum= getOrderNum();

//do something withou orderNum here

call(orderNum);

上例中的注释处代表了一段和orderNum不相关的代码。orderNum的声明和初始化离被使用的地方相隔了很多行的代码,这样做不好,不如这样:

//do something withou orderNum here

int orderNum= getOrderNum();
call(orderNum);

4.10 缩小变量的作用域

能用局部变量的,不要使用实例变量,能用实例变量的,不要使用类变量。变量的生存期越短,以为着它被误用的机会越小,同一时刻程序员要关注的变量的状态越少。实例变量和类变量默认都不是线程安全的,局部变量是线程安全的。比如如下代码:

public class OrderPayAction{
    private Order order;

    public void doAction() {
        order = orderDao.findOrder();
        doJob1();
        doJob2();
    }
   
    private void doJob1() {
        doSomething(order);
    }
   
    private void doJob2() {
        doOtherThing(order);
    }
}

上例中order只不过担当了在方法间传递参数之用,用下面的方法更好:

public class OrderPayAction{

    public void doAction() {
        order = orderDao.findOrder();
        doJob1(order);
        doJob2(order);
    }
   
    private void doJob1(Order order) {
        doSomething(order);
    }
   
    private void doJob2(Order order) {
        doOtherThing(order);
    }
}

4.11 尽量不要用参数来带回方法运算结果

比如:

public void calculate(Order order) {
    int result = 0;
    //do lots of computing and store it in the result
   
    order.setResult(result);
}

public void action() {
    order = orderDao.findOrder();
    calculate(order);

    // do lots of things about order
}

例子中calculate方法通过传入的order对象来存储结果, 不如如下写:

public int calculate(Order order) {
    int result = 0;
    //do lots of computing and store it in the result
   
    return result;
}

public void action() {
    order = orderDao.findOrder();
    order.setResult(calculate(order));

    // do lots of things about order
}
  • Share/Bookmark

为什么java没有php开发web容易?

June 2nd, 2009

为什么java没有php开发web容易?原因可能有很多种,就我这半年的php经验来看,主要有以下几点原因:

1. java没有php那样好用的map(在php里叫做array)

这一点很重要,直接看代码吧:
php的:

$values= array(
    'login_name'=>'sulong',
    'password'=>'',
    'login_result'=>array(
        'error_no'=>3,
        'msg'=> 'account disabled'
    )
);

java的:

Map values = new HashMap();
values.put("login_name", "sulong");
values.put("password", "");

Mapresult = new HashMap();
result.put("error_no", "3");
result. put("msg", "account disabled");

values.put("login_result", result);

很多时候,java程序员宁愿选择建一个新的类来做数据的载体,也就是常说的bean了。但是很多时候,我只想要一个简洁好用的Map而已。

最好的是groovy:

values = [
    login_name: "sulong",
    password: "",
    login_result: [
        error_no: "3",
        msg: "account disabled"
    ]
]

仅凭这一点,我就觉得在groovy克服其它问题后,必会成为java在web开发上的利器。

2. java经常要重启服务器

用java开发web,只要改动了java代码,就要重新启动服务器,至少要重新部署一下应用,才能看到新代码的效果,而php就不需要。所以,开发php的时候,经常是一边写,一边运行看效果,快速的迭代和开发。相比之下,java要不断的编译重新发布,重启服务器,比php慢多了。如果java开发也可以即时生效,不用不断的重启的话,那么java的开发效率必然会有很大的提升。

3. 动态类型

确实,动态类型意味这较弱的IDE支持,意味着要进行更多的测试,以避免意想不到的效果,但是,另一方面,动态类型将代码简化,让程序员关注于做什么,而不是怎么做,提升了生产率。比如,用动态类型语言的时候,我们只要说,dog.bark(),那么我们不用关心dog到底是不是dog,只要他能bark(),那bark()就好了。而在静态类型的语言中,我们一定要先搞清楚那是什么,能不能bark,如果不能bark怎么办?是不是要做一层包装?做类型的转换?等等。严谨固然有他的好处,但自由简明显然开发起来更加的快速。

  • Share/Bookmark

企业应用集成–java

April 28th, 2009

java企业应用最终往往运行在某种应用服务器中,比如tomcat,jboss等。部署的单元是war, jar 或者 ear文件,开始的时候,我们可能会将多个应用放到一个单元里来开发管理和部署。随着应用的增多,为了能够让每个应用能够独立的运行不受干扰,也为了开发工作能很好的安排,往往会将各应用分开,于是会有多个war, jar,或ear文件。试想一下,假设你有订单系统,产品系统,客服系统,等,如果这些应用都被放在同一个部署单元里,当需要更新客服系统时,会短暂的导致订单系统不能正常工作,而这种情况往往是不能被接受的。另外,如果客服系统非常繁忙,由于部署在同一台物理服务器上,共享硬件资源,订单系统会受到干扰,而这也不是我们想要的。将单独的应用单独打包,单独部署成为必然。那么就面临着让这些应用通信,将它们集成的问题。


与java和php之间集成相比,java与java之间的集成,不仅可以使用共享数据库,soap,或基于http的简单的文档交换,还可以使用jms,rmi等多种方案。其中jms是j2ee众多的标准中最为成功的协议之一,很少有人针对它发表过不满意见。我们在工作中就采用了jms。和http相比,它有如下优势:

为java量身定做的

jms不仅可以传输普通的字符串,还可以直接传输java对象,并和java的其它服务结合起来,接口api也符合java的使用习惯。如果用http,我们还得要考虑如何把对象序列化等问题,比较麻烦。

使用长连接

http主要用在客户端和服务器交互上,为了能让服务器支持大量的客户端,http往往都以一种无状态和短连接的方式工作。而jms主要用在java应用之间的交互,不需要支持那么多的客户端,用长连接更加适合。典型的http服务器,可能同时要给数千数万人提供服务,如果服务器和每个客户端保持一个连接,那对服务器来说,显然是一个非常大的负担。所以,通常客户端和http的一次交互完成后,连接就断开,而下次访问时,还得要重新建立连接。jms服务器面对的客户端一般只有几个,至多几十个,和每个客户端保持连接的开销是可以接受的。典型的企业可能有如下系统:订单系统,产品系统,结算系统,等等,这些系统,每个都和集中的jms服务器保持一个连接,总连接数也是很少的,所以可以保持长连接。这客户端和jms服务器通信的时候,不必每次都建立连接,减少了网络操作,提升了性能。

异步

http操作的过程是同步的,如果要使用异步的方式工作,需要程序员再做些编码工作。而jms天生就以异步的方式来工作的,不必做过多的编码。消息的生产者一旦把消息发送出去,就可以立刻返回不必等待消息的消费结果,后续的消息消费可以自动的异步执行。将不必要立刻执行的动作异步执行,可以大大降低系统的响应时间。

可靠的消息传递

在使用http通信的过程中,如果发生了网络异常,是否要做重发,以及如何重发,会是一件很痛苦的事情。jms提供了persitent的传递模式,在这种模式时,客户端代理在发送消息之前,会先把消息持久,如果发生网络异常,客户端代理会自动处理重发,而不需要应用程序关心。jms消息的唯一编号,可以有效的阻止一个消息被重复处理的问题。

在java应用集成时,jms比http更适合。成熟的jms服务器软件很多,除了ibm的mq,jboss mq, apache activemq 都是很不错的选择。

  • Share/Bookmark