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