每隔x分钟我想查询新实例并缓存结果。我目前只需要一个简单的缓存解决方案,因此我想更新Set
@ApplicationScoped
CacheBean
我试过了:
ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(1);
ScheduledFuture<?> sf = scheduler.scheduleAtFixedRate(new Runnable() {
public void run() {
//.................
但是创建的线程无法访问任何上下文实例(InvocationException)。
那么如何以CDI / JPA的方式做到这一点?
使用Tomcat 7,Weld,JPA2 - Hibernate。
答案 0 :(得分:2)
我的建议是尝试已集成CDI和JPA的Tomcat版本(TomEE)。它附带OpenJPA,但您可以使用Hibernate。然后用这样的类进行缓存:
@Singleton
@Startup
public class CachingBean {
@Resource
private BeanManager beanManager;
@Schedule(minute = "*/10", hour = "*")
private void run() {
// cache things
}
}
该组件会在应用启动时自动启动,并且每十分钟运行一次上述方法。有关详细信息,请参阅Schedule文档。
为你破解了一个例子。使用一个不错的CDI / EJB组合来安排CDI事件。
实际上,这是围绕BeanManager.fireEvent(Object,Annotations...)
方法的简单包装,可将ScheduleExpression
添加到组合中。
@Singleton
@Lock(LockType.READ)
public class Scheduler {
@Resource
private TimerService timerService;
@Resource
private BeanManager beanManager;
public void scheduleEvent(ScheduleExpression schedule, Object event, Annotation... qualifiers) {
timerService.createCalendarTimer(schedule, new TimerConfig(new EventConfig(event, qualifiers), false));
}
@Timeout
private void timeout(Timer timer) {
final EventConfig config = (EventConfig) timer.getInfo();
beanManager.fireEvent(config.getEvent(), config.getQualifiers());
}
// Doesn't actually need to be serializable, just has to implement it
private final class EventConfig implements Serializable {
private final Object event;
private final Annotation[] qualifiers;
private EventConfig(Object event, Annotation[] qualifiers) {
this.event = event;
this.qualifiers = qualifiers;
}
public Object getEvent() {
return event;
}
public Annotation[] getQualifiers() {
return qualifiers;
}
}
}
然后使用它,将Scheduler
作为EJB注入并安排离开。
public class SomeBean {
@EJB
private Scheduler scheduler;
public void doit() throws Exception {
// every five minutes
final ScheduleExpression schedule = new ScheduleExpression()
.hour("*")
.minute("*")
.second("*/5");
scheduler.scheduleEvent(schedule, new TestEvent("five"));
}
/**
* Event will fire every five minutes
*/
public void observe(@Observes TestEvent event) {
// process the event
}
}
完整源代码和工作示例here。
如果有10个观察者,每个观察者需要7分钟才能执行,那么一个事件的总执行时间为70分钟。如果安排该事件的发生频率超过70分钟,那绝对没有用。
如果你做了会怎么样?取决于@Singleton
@Lock
政策
@Lock(WRITE)
是默认设置。在此模式下,timeout
方法基本上将被锁定,直到上一次调用完成。即使每70分钟只能处理一次,每5分钟启动它最终会导致所有池化的计时器线程等待你的Singleton。@Lock(READ)
允许并行执行timeout
方法。事件会并发一段时间。然而,由于它们实际上每个需要70分钟,所以在一个小时左右的时间内,我们将在计时器池中耗尽线程,就像上面一样。优雅的解决方案是使用@Lock(WRITE)
然后在@AccessTimeout(value = 1, unit = TimeUnit.MINUTES)
方法上指定一些短暂的超时,例如timeout
。当下一个5分钟的调用被触发时,它会等到1分钟才能在放弃之前访问Singleton。这将使您的计时器池不会填满备份作业 - “溢出”就会被丢弃。
答案 1 :(得分:0)
而不是将新的Runnable(){....}传递给scheduler.scheduleAtFixedRate,而是创建一个实现Runnable和@Inject该bean的CDI bean,然后将其传递给scheduler.scheduleAtFixedRate
答案 2 :(得分:0)
与David Blevins聊了一会儿后,我可以承认他的答案是我投票的好人。非常感谢所有这一切。尽管大卫,你忘了宣布你参与了TomEE,我知道他总是打扰别人。
无论如何,我去的解决方案是由Mark Struberg在#Deltaspike(freenode)中提出的。
作为一名deltaspike用户,我很高兴与deltaspike一起做。此博客文章中概述了解决方案:
http://struberg.wordpress.com/2012/03/17/controlling-cdi-containers-in-se-and-ee/
我必须切换到OWB,请参阅https://issues.apache.org/jira/browse/DELTASPIKE-284
干杯