我在想如何通过jndi节省查找远程ejb参考的时间。我有一个需要快速工作的应用程序,但它也必须调用远程ejb,这会降低它的速度。
所以我的解决方案是这样的: 我使用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返回游泳池
finally {
try {
pool.returnObject(someEjb);
} catch (Exception e) {
logger.error("Could not return object to pool.", e);
}
}
据我所知,无法保证远程引用将保持连接,因此如果我们使用缓存的远程ejb捕获异常,我们只会使该对象无效并重试。
您如何看待这种方法?这是对的吗?也许其他一些解决方案,建议?
答案 0 :(得分:5)
我正在回答JBoss AS,因为我对其他AS的经验有限:
远程JNDI引用只是(负载平衡)无连接代理(请参阅JBoss clustering proxy architecture)。序列化它们很好,这意味着您可以将它们保存为其他EJB中的成员,并像您一样缓存它们(我不知道您的池是否序列化了您的对象,某些缓存是否存在)。
关于代理无效: 代理将仅在方法调用期间打开连接,因此本身没有“连接”状态。代理还可以具有多个IP地址和负载平衡。在JBoss中,节点列表在每次方法调用时都会动态更新,因此引用过时的风险很小。如果所有节点都关闭或代理保持不活动状态而所有节点IP地址都过时,则仍有可能发生这种情况。根据池重用策略(LRU或其他?),其余缓存代理一旦出现无效的概率会有所不同。公平策略将最大限度地降低池中包含非常旧条目的风险,您希望在此方案中避免使用这些条目。
在制定公平政策的情况下,出于同样原因而变得陈旧的可能性会增加,而“一旦一个人过时的清晰汇集”政策就有意义了。此外,您需要考虑其他节点的情况。就像现在一样,你的算法将进入忙循环查找引用,而另一个节点关闭。我将为重试实现指数退避,或者只是将其视为致命的失败并使异常成为运行时异常,具体取决于您是否可以使用远程EJB消失一段时间。并使您捕获的异常(如RemoteCommunicationFailedException),避免捕获一般异常或错误,如异常,错误或Throwable。
您必须问自己的另一个问题是您想要的并发数量。通常,代理对于SLSB是线程安全的,而对于SFSB则是单线程。 SFSB本身不是线程安全的,并且SLSB默认情况下序列化访问。这意味着除非您启用对EJB 3.1 bean的并发访问(请参阅tss link),否则每个线程需要一个远程引用。即:池化N个SLSB远程引用将为您提供N个线程并发访问。如果启用并发访问并将SLSB编写为带有注释@ConcurrencyAttribute(NO_LOCK)
的线程安全bean,则只需一个代理即可获得无限的并发,并删除整个池。你的选择。
编辑:
不需要任何限制 反对并发客户端访问 无状态会话bean因为 容器将每个请求路由到a 不状态的不同实例 会话bean类。
这意味着您根本不需要任何池。只需使用一个远程参考。
答案 1 :(得分:5)
来自规范
3.4.9 并发访问会话Bean参考
这是允许的 获取会话bean引用和 尝试调用相同的引用 对象同时来自多个 线程。但是,由此产生的客户端 每个线程上的行为取决于 。的并发语义 目标bean。有关详细信息,请参见第4.3.14节和第4.8.5节 会话bean的并发行为。
§4.3.14的摘要:
如果bean是 SLSB ,则每个调用都将由应用程序中的一个EJB提供。服务器池。该应用程序。服务器同步调用EJB实例,因此永远不会同时访问每个EJB实例。
对于 SFSB ,每次调用都会调度到一个特定的EJB实例和应用程序。服务器不同步通话。因此,对远程引用的两个并发调用可能会导致对EJB实例的并发访问,然后引发javax.ejb.ConcurrentAccessException
。客户端负责正确同步对远程引用的访问。
§4.8.5是关于EJB单例的,可能不是你正在使用的。
我假设您使用SLSB,因此您不需要在客户端拥有池:查找远程bean一次,并使用来自多个线程的相同引用。
然而,您可以执行基准以查看使用多个引用是否可以提高性能,但是与远程调用本身的成本相比,增益(如果有)可能是可忽略的。
如果那时你仍然决定有多个远程引用,我会建议另一个设计。基于您的问题,我假设您有一个多线程应用程序。您可能已经为线程使用池,因此参考池可能是多余的。如果每个线程在创建时获得远程引用,并且线程被池化,则不会有那么多远程查找并且简化了设计。
我的2美分