Java 异步EJB应用程序中Hibernate的奇怪行为。比赛条件?
问题的简要说明: 我正在开发JavaEE应用程序来处理来自金融市场的消息。应用程序部署在应用程序服务器上:Wildfly-8.2.0.Final 应用程序中的消息流如下图所示:Java 异步EJB应用程序中Hibernate的奇怪行为。比赛条件?,java,hibernate,jakarta-ee,jpa,concurrency,Java,Hibernate,Jakarta Ee,Jpa,Concurrency,问题的简要说明: 我正在开发JavaEE应用程序来处理来自金融市场的消息。应用程序部署在应用程序服务器上:Wildfly-8.2.0.Final 应用程序中的消息流如下图所示: MDB1 \ StrategyManager(@Singleton) -> StrategyRunner(@Singleton) -> SomeStrategy(@Singleton) / MDB2 异步调用的EJB-SomeStrategy(@Singleton)正在对定义
MDB1
\
StrategyManager(@Singleton) -> StrategyRunner(@Singleton) -> SomeStrategy(@Singleton)
/
MDB2
异步调用的EJB-SomeStrategy(@Singleton)正在对定义为JPA实体的JPA模型执行读取更新操作:
StrategyEntity-@Slf4j
@Singleton
@Local
@Startup
public class StrategyRunner {
@EJB
private SomeStrategySingletonEJB someStrategySingletonEJB;
@EJB
private StrategyDao strategyDao;
public void runStrategy(StrategyEntity strategyEntity, OrderBookAggregated orderBookAggregated) {
strategyDao.refresh(strategyEntity);
log.info("StrategyEntity after refresh: {}", startegyEntity);
if (!strategyEntity.isRunning()) return;
someStrategySingletonEJB.updateOnOrderBook(strategyEntity);
log.info("StrategyEntity before save: {}", startegyEntity);
strategyDao.save(strategyEntity);
}
public void runStrategy(StrategyEntity strategyEntity, OrderQueryReport orderQueryReport) {
strategyDao.refresh(strategyEntity);
log.info("StrategyEntity after refresh: {}", startegyEntity);
if (!strategyEntity.isRunning()) return;
someStrategySingletonEJB.updateOnExecutionReport(strategyEntity, orderQueryReport);
log.info("StrategyEntity before save: {}", startsingletoneegyEntity);
strategyDao.save(strategyEntity);
}
}
方法runStrategy()是从MDB并发调用的。Singleton的默认锁类型是写锁,因此方法应该
不要同时运行。StrategyEntity是从上层数据库(StrategyManager)中检索的,请参见详细说明。
问题是:有时(非常落后-平均2000条消息中有一条)“保存前策略实体”不同
“刷新后的战略实体”它看起来像某种比赛状态。如果我替换strategyDao(访问RDBMS)
通过将简单缓存实现为EJB@Singleton,问题就消失了。我推断出这个问题
将数据库层替换为缓存后,作为应用程序的Hibernate操作可以在繁重的工作负载下完美工作。你有什么想法吗
休眠属性:
<persistence-unit name="AlgorithmEnginePU">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<jta-data-source>java:jboss/AlgorithmEngineDS</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<!--<class>com.main.model.configuration.ExchangeInformation</class>-->
<properties>
<!-- Properties for Hibernate -->
<property name="hibernate.default_schema" value="algorithm_engine"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform"/>
<!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
</properties>
</persistence-unit>
第二个MDB接收其他类型的消息,并将它们排队,以便在注入的单例上处理它们
遵循默认写入锁定:
@Slf4j
@MessageDriven(name = "MessageReceiver", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue = "jms/tradeAgentReply"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Topic"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge")})
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class TradeAgentClientMDB implements MessageListener {
@EJB
private StrategyManager strategyManager;
@Override
public void onMessage(Message message) {
(...)
try {
TradeQueryReport tradeQueryReport = (TradeQueryReport) serializableMessage;
if (tradeQueryReport instanceof OrderQueryReport ) {
OrderQueryReport orderQueryReport = (OrderQueryReport) tradeQueryReport;
strategyManager.updateOrder(orderQueryReport);
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
以下是EJB(StrategyRunner@EJB在简要描述部分中描述):
代码本身看起来很好,我找不到任何明显的缺陷。我记得不久前我自己也遇到过类似的情况,日志输出与数据库中的内容不匹配。以下是我的情况:
- 日志语句。这并没有立即记录下来;而是收集数据并将日志事件添加到队列中。这意味着日志事件具有对对象的硬引用
- 插入/更新数据库
- 交易结束
- 第二个线程从缓存中获取对象
- 对象被修改
- 另一个线程开始处理日志事件
- 日志事件已转换为字符串李>
log.info("StrategyEntity after refresh: {}", startegyEntity.toString());
在您的
OrderBookReceiver
中,您从未使用过orderBookEntity
。相反,有一个新变量orderBookAggregated
。打字错误?问题:你怎么知道StrategyRunner
是单身汉?也要确保锁是真正的单体锁-只是为了安全。要确保日志条目不是来自不同的线程,请将线程ID添加到日志文件中。最后,你是同步登录的吗?是的,这是一个输入错误。我选对了,谢谢!我不知道我是否正确理解你的问题。。。StrategyRunner是一个单例,因为它是用EJB@Singleton注释的。锁也是单例的,默认锁类型更改为READ(我还附上了它的代码-请参阅StrategyOrderBookLock)。出于测试目的,系统中只有一个strategyEntity,因此来自MDB的每条消息都会修改同一个实体。日志条目来自不同的线程,因为单例调用由来自Wildfly线程池的不同线程提供服务。在Spring中,配置可以相互覆盖。如果EJB可以做到这一点,那么您应该检查bean是否定义了两次,可能是不同的作用域。非常感谢您提供的线索,但主要问题不是日志与数据库中实体的状态不同。主要的问题是,有时(很少)应用程序从数据库中读取旧状态,其逻辑流从此点开始损坏。作为一种解决方法(对ORM的某些错误进行推理),我用简单的单例缓存替换了数据库。在这个变通应用程序工作正常之后(所有实体都存储在单例缓存中,而不是存储在数据库中)。目前我正在考虑用EclipseLink替换Hibernate(作为Wildfly中的默认ORM),并测试这个问题是否是由ORM引起的。
@Slf4j
@Singleton
@Local
@Startup
public class StrategyManager {
@Inject
private StrategyDao strategyDao;
@EJB
private StrategyRunner strategyRunner;
@Asynchronous
public void updateOrderBook(OrderBookAggregated orderBookAggregated) {
StrategyEntity strategy = strategyDao.findStrategyByExchange(orderBookAggregated.getAccount().getExchangeEntity());
if(strategyEntity == null) return;
strategyRunner.runStrategy(strategyEntity, orderBookAggregated);
}
@Asynchronous
public void updateOrder(OrderQueryReport orderQueryReport) {
StrategyEntity strategy = strategyDao.findStrategyByOrder(orderQueryReport.getClientOrderId());
if(strategy == null) return;
strategyRunner.runStrategy(strategy, orderQueryReport);
}
(...)
}
@Singleton
@Local
@Startup
public class StrategyOrderBookLock {
private java.util.concurrent.locks.Lock updateOrderBookLock = new ReentrantLock();
@Lock(LockType.READ)
public boolean tryLock() {
return updateOrderBookLock.tryLock();
}
@Lock(LockType.READ)
public void unlock() {
updateOrderBookLock.unlock();
}
}
log.info("StrategyEntity after refresh: {}", startegyEntity.toString());