2010-06-22 32 views
7

我在想如何节省查找通过jndi远程ejb参考的时间。我有一个需要工作得非常快的应用程序,但它也必须调用远程ejb,这会降低速度。缓存远程EJB 3.0参考

所以我的解决方案是这样的: 我带了apache commons-pool库,并使用它的StackObjectPool实现了我的远程ejb引用缓存。

private static final ObjectPool pool = new StackObjectPool(new RemoteEjbFactory()); 

厂看起来是这样的:

public static class RemoteEjbFactory extends BasePoolableObjectFactory { 

    @Override 
    public Object makeObject() { 
     try { 
      return ServiceLocator.lookup(jndi); 
     } catch (NamingException e) { 
      throw new ConfigurationException("Could not find remote ejb by given name", e); 
     } 
    } 
} 

然后我把对象从池借用它(如果在池中没有免费的对象,它使用工厂创建一个):

SomeEjbRemote someEjb = null; 
try { 
     someEjb = (SomeEjbRemoteImpl) pool.borrowObject(); 
     someEjb.invokeRemoteMethod(); 
} catch (Throwable t) { 
     if (someEjb != null) { 
      pool.invalidateObject(someEjb); 
     } 
     pool.clear(); // Maybe its not neccessary 
     someEjb = (SomeEjbRemoteImpl) pool.borrowObject(); 
     someEjb.invokeRemoteMethod(); // this time it should work 
} 

当然返回ejb后成功invokacion

finally { 
    try { 
     pool.returnObject(someEjb); 
    } catch (Exception e) { 
     logger.error("Could not return object to pool.", e); 
    } 
} 

据我所知,不能保证远程引用将保持连接,所以如果我们使用缓存远程ejb捕获异常,我们只是使该对象无效并重试。

您对这种方法有什么看法?这是对的吗?也许还有其他解决方案,建议?

回答

5

从规范

3.4.9 到Session Bean的并发访问参考

这是permissable到 获得一个会话bean参考和 试图从并发调用相同的参考 对象多个 线程。但是,每个线程上产生的客户端行为取决于 目标Bean的并发语义。有关会话bean的并发行为的详细信息,请参见第4.3.14节和第4.8.5节。

§4.3.14摘要:

如果bean是SLSB,每次通话将被在应用一个EJB提供服务。服务器池。应用程序。服务器同步对EJB实例的调用,因此每个EJB实例都不会同时被访问。

对于SFSB,每个调用都调度到一个特定的EJB实例和应用程序。服务器不会同步该呼叫。因此,对远程引用的两个并发调用可能导致并发访问EJB实例,然后引发javax.ejb.ConcurrentAccessException。客户端负责正确同步对远程引用的访问。

而§4.8.5是关于EJB单例,可能不是你正在使用的。

我假定你使用SLSB,所以你不需要在客户端有一个池:查找一次远程bean,并使用多个线程中的同一个引用。

你可以然而做基准,看是否使用多个基准提高性能,但增益 - 如果有的话 - 可能是忽略比较远程调用本身的成本。

如果你仍然决定有多个远程引用,我会建议其他设计。根据你的问题,我假设你有一个多线程的应用程序。您很有可能已使用为线程使用池,因此用于参考的池可能是多余的。如果每个线程在创建时都获得远程引用,并且汇集了线程,则不会有那么多的远程查找,并且设计也会简化。

我的2美分

5

我在回答JBoss AS,因为我对其他AS的经验有限:

远程JNDI引用只是(负载平衡)无连接代理(请参阅JBoss clustering proxy architecture)。对它们进行序列化并没有问题,这意味着您可以将它们保存为其他EJB中的成员,并像您一样缓存它们(我不知道您的池是否序列化对象,有些缓存可以)。

关于代理失效: 代理将仅在方法调用期间打开连接,因此本身不具有“已连接”状态。这些代理还可以拥有多个IP地址和负载平衡。在JBoss中,节点列表在每次方法调用时都会动态更新,因此引用过期的风险很小。如果所有节点停机或代理保持不活动状态,而所有节点IP地址过期,仍然有可能发生这种情况。根据池重用策略(LRU或其他?),一旦其他缓存代理无效,其概率将有所不同。一个公平的政策将最大限度地减少在池中拥有非常旧的条目的风险,在这种情况下你希望避免这种风险。

随着公平的政策的到来,出于同样的原因所有陈旧的可能性增加,你的'明确的池一旦陈旧'的政策将是有道理的。另外,您需要考虑其他节点的情况。就像现在一样,你的算法会进入一个忙环查找参考,而另一个节点关闭。我会为重试实现一个指数回退,或者只是认为它是一个致命的故障,并且使异常成为运行时异常,具体取决于您是否可以使用远程EJB暂时搁置一段时间。并让异常捕获特定的(如RemoteCommunicationFailedException),避免捕获泛型异常或像Exception,Error或Throwable等错误。

你必须问自己的另一个问题是你想要的并发量。通常,代理对于SLSB而言是线程安全的,对于SFSB而言,代理对于单线程是安全的。 SFSB本身不是线程安全的,并且SLSB按默认顺序访问。这意味着,除非启用对EJB 3.1 bean的并发访问(请参阅tss link),否则每个线程需要一个远程引用。即:汇集N个SLSB远程引用将为您提供N个线程的并发访问。如果启用并发访问并将您的SLSB编写为带注释@ConcurrencyAttribute(NO_LOCK)的线程安全bean,那么只需一个代理就可以实现无限并发,然后删除整个池。你的选择。

编辑:

ewernli是正确的,线程安全SLSB代理创建每次调用服务器上的一个新的实例。这是在4.3.14规定:

无需任何限制 对并发客户端访问 无状态会话bean因为 集装箱航线每个请求无状态 会话bean类的 不同实例。

这意味着你根本不需要任何池。只需使用一个远程参考。

+0

谢谢你的回答! – nesvarbu 2010-06-23 12:28:34

+1

是否可以对单个EJB或所有远程EJB执行有限数量的并发远程调用? JBoss是否通过配置参数来控制它? – 2016-06-15 18:42:02