我已经在我的java EE应用程序中实现了断路器模式,并且由于它保持了一个状态(失败的请求数,平均响应时间,锁定/解锁等),因此我将inte设置为@Stateful
。 为了避免由于容器锁定而导致序列化的问题,我添加了@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
并确保所有操作都是线程安全的。
所以它目前看起来像这样:
@Stateful
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
public class CatalogBreaker extends Breaker {
但我仍然得到。
Caused by: javax.ejb.ConcurrentAccessTimeoutException: WFLYEJB0228: EJB 3.1 FR 4.3.14.1 concurrent access timeout on CatalogBreaker - could not obtain lock within 5000 MILLISECONDS
但是,如果我正确理解了所有内容,CuncurrencyManagement
注释应该将锁定委托给 bean 而不是容器......既然CatalogBreaker
没有任何锁或类似的东西,那么我怎么会出现这个错误呢?
据我所知,有状态的 EJB 总是被写锁锁定,但我很难找到任何官方的 Oracle 文档来说明这一点(因此语言:"据我所知")。
换句话说,批注@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
什么都不做。对有状态 EJB 业务方法的同一实例的每次调用都将阻塞下一个实例,因此有状态 EJB 的两个业务方法不能同时执行。
如果您有多个线程尝试同时访问有状态 EJB,容器将阻止它们这样做,这解释了您的性能损失。
默认情况下,超时为 5 秒,日志中也指示为默认值。您可以使用AccessTimeout 调整值,但我相信大多数人会认为这样做是不好的做法。我想大多数人都会同意,如果你遇到超时,那么你需要重新考虑你的设计。默认值为 5 秒的原因是,容器期望所有工作都是短期任务。您遇到超时,不是因为您的工作花费的时间太长,而是因为您使用有状态 EJB 的方式不符合容器期望您使用它的方式。不是说你从字面上做事的方式是错的,只是不对......有点像容器在爸爸的声音里说:我没有生气,只是失望。