想给 GlassFish Server 添加定时器,不知从何下手
Want to add a timer to GlassFish Server, don't know where to start
我正在尝试使用 Glassfish 服务器实现 Java EE websockets,我已经掌握了基础知识。
至于我当前的项目,我想像在游戏中一样实现服务器 "ticks",每隔几毫秒更新一次服务器,以便连接到服务器的所有会话看到相同的内容。
但是,我不知道从哪里开始使用 Glassfish 进行这项工作。我知道我从头开始创建一个服务器并实现一个计时器方法,但如果可能的话,我宁愿在这里为自己节省一些工作。
简单的说,glassfish server(假设是运行)实现一个timer/tick机制容易吗?
例如,我想让服务器在通过 websocket 连接后定期向客户端 HTML 页面发送单词 "hello"。
精简版
我们用三个 classes 解决了它:
- 调度器class:发送事件的时钟
- Websocket 端点:基本的 websocket 会话处理程序
- Websocket 会话处理程序:捕获事件并管理所有 websocket 会话的指挥者
带代码的长版本
现在是代码,1) 是 Mike 在评论中提到的代码:一个 classic Scheduler,它触发了一些事件。 class 基本上是一个时钟,它会敲响正在收听其事件的任何人。我们有一个 EAR=EJB+WAR 项目。我们的定时器在 EJB 模块中,但如果你有一个 WAR,你可以把它放在那里。
// some import
import javax.enterprise.event.Event;
@Singleton
@Startup
public class TimerBean {
@Inject
private Event<TickEvent> tickEvent;
@Schedule(hour = "*", minute = "*", second = "*/5")
public void printSchedule() {
tickEvent.fire(new TickEvent(/*...your initialization...*/));
}
public class TickEvent{
// we have some info here but you can
// leave it empty.
}
}
对于2)websocket端点来说,这是一个很学术的端点。不要忘记一个连接 = 一个端点实例。您可以使用 session.getOpenedSessions()
从给定会话中找到所有打开的会话,但我将在下一部分解释为什么我使用处理程序。
// some import
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
@ServerEndPoint(value = "/myEndPointPath")
public class TickEndPoint{
@Inject
private TickSessionHandler sessionHandler;
@OnOpen
public void onOpen(Session session){
sessionHandler.addSession(session);
}
@OnClose
public void onClose(Session session, CloseReason reason){
sessionHandler.removeSession(session);
}
}
现在是重点:会话处理程序。会话处理程序是一个 ApplicationScoped bean,因此它始终可用。我使用它的原因:
- 触发 TickEvent 时,您不知道 TickEndPoint 实例是否存在,因此事件捕获可能会引发一些愚蠢的错误。我们用
@Observes(notifyObserver = Reception.IF_EXISTS)
修复了它,但我们遇到了其他 EJB 问题,我将在 2. 中详细介绍
- 由于我尚未确定的原因,当我在我的@ServerEndPoint 中使用@Observes 时,我所有的@EJB 注入都失败了,所以我将@EJB 注入切换为@Inject。但是由于 ApplicationScoped 是一个 CDI bean,@EJB 和@Observes 在会话处理程序中可以很好地协同工作 class。我们并不真正需要 @EJB 注释本身,但为了与其他 bean 的设计一致性,我们希望我们所有的 EJB 都使用 @EJB 进行注释。
- 监控:您可以进行监控,例如您在时间 T 发送了多少 "hello",给您一个时间与连接会话数的图表。
另外,在我们的特殊情况下,我们有一些要点,您稍后可能会遇到。
- 我们需要 select SESSIONS 的特定子集,而不是全部。在 @ServerEndPoint 中设置一个静态 Set 会导致某些东西将我变成绿巨人行为,尤其是当您具有 EJB 依赖项时。在一个游戏示例中,假设您有蓝队和红队夺旗场景。如果红队夺旗,需要发送"Oops, we lost the flag"给蓝队,"Yeah we got it"给红队。
- 监控:除了连接时间之类的,我们还需要"last activity time"之类的信息。要将它正确地放入 JSF 数据表中,CDI bean 中的 list/set 是最佳选择
我遇到了一些tutorial/detail,其中@ServerEndPoint 被注释为@ApplicationScoped。按照设计(一个服务器端点 = 一个 websocket 连接),我对此感到不舒服,所以我拒绝实施此解决方案
@Named
@ApplicationScoped
public class TickSessionHandler{
// There is no need to have a static Set, worst,
// containers don't like it
public Set<Session> SESSIONS;
// let's initialize the set
public TickSessionHandler{
this.SESSIONS = new HashSet<>();
}
// ---------- sessions management
public void addSession(Session session){
this.SESSIONS.add(session);
}
public void removeSession(Sesssion session){
this.SESSIONS.remove(session);
}
// ---------- Listen to the timer
public void onTick(@Observes TickEvent event){
// if required, get the event attribute
// and proceed
// your request:
this.SESSIONS.forEach(session -> {
session.getBasicRemote().sendText("hello");
});
}
}
希望对您有所帮助。解释看起来很长,但核心实现其实很轻。据我所知,这种要求没有捷径。
我正在尝试使用 Glassfish 服务器实现 Java EE websockets,我已经掌握了基础知识。
至于我当前的项目,我想像在游戏中一样实现服务器 "ticks",每隔几毫秒更新一次服务器,以便连接到服务器的所有会话看到相同的内容。
但是,我不知道从哪里开始使用 Glassfish 进行这项工作。我知道我从头开始创建一个服务器并实现一个计时器方法,但如果可能的话,我宁愿在这里为自己节省一些工作。
简单的说,glassfish server(假设是运行)实现一个timer/tick机制容易吗?
例如,我想让服务器在通过 websocket 连接后定期向客户端 HTML 页面发送单词 "hello"。
精简版
我们用三个 classes 解决了它:
- 调度器class:发送事件的时钟
- Websocket 端点:基本的 websocket 会话处理程序
- Websocket 会话处理程序:捕获事件并管理所有 websocket 会话的指挥者
带代码的长版本
现在是代码,1) 是 Mike 在评论中提到的代码:一个 classic Scheduler,它触发了一些事件。 class 基本上是一个时钟,它会敲响正在收听其事件的任何人。我们有一个 EAR=EJB+WAR 项目。我们的定时器在 EJB 模块中,但如果你有一个 WAR,你可以把它放在那里。
// some import
import javax.enterprise.event.Event;
@Singleton
@Startup
public class TimerBean {
@Inject
private Event<TickEvent> tickEvent;
@Schedule(hour = "*", minute = "*", second = "*/5")
public void printSchedule() {
tickEvent.fire(new TickEvent(/*...your initialization...*/));
}
public class TickEvent{
// we have some info here but you can
// leave it empty.
}
}
对于2)websocket端点来说,这是一个很学术的端点。不要忘记一个连接 = 一个端点实例。您可以使用 session.getOpenedSessions()
从给定会话中找到所有打开的会话,但我将在下一部分解释为什么我使用处理程序。
// some import
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
@ServerEndPoint(value = "/myEndPointPath")
public class TickEndPoint{
@Inject
private TickSessionHandler sessionHandler;
@OnOpen
public void onOpen(Session session){
sessionHandler.addSession(session);
}
@OnClose
public void onClose(Session session, CloseReason reason){
sessionHandler.removeSession(session);
}
}
现在是重点:会话处理程序。会话处理程序是一个 ApplicationScoped bean,因此它始终可用。我使用它的原因:
- 触发 TickEvent 时,您不知道 TickEndPoint 实例是否存在,因此事件捕获可能会引发一些愚蠢的错误。我们用
@Observes(notifyObserver = Reception.IF_EXISTS)
修复了它,但我们遇到了其他 EJB 问题,我将在 2. 中详细介绍
- 由于我尚未确定的原因,当我在我的@ServerEndPoint 中使用@Observes 时,我所有的@EJB 注入都失败了,所以我将@EJB 注入切换为@Inject。但是由于 ApplicationScoped 是一个 CDI bean,@EJB 和@Observes 在会话处理程序中可以很好地协同工作 class。我们并不真正需要 @EJB 注释本身,但为了与其他 bean 的设计一致性,我们希望我们所有的 EJB 都使用 @EJB 进行注释。
- 监控:您可以进行监控,例如您在时间 T 发送了多少 "hello",给您一个时间与连接会话数的图表。
另外,在我们的特殊情况下,我们有一些要点,您稍后可能会遇到。
- 我们需要 select SESSIONS 的特定子集,而不是全部。在 @ServerEndPoint 中设置一个静态 Set 会导致某些东西将我变成绿巨人行为,尤其是当您具有 EJB 依赖项时。在一个游戏示例中,假设您有蓝队和红队夺旗场景。如果红队夺旗,需要发送"Oops, we lost the flag"给蓝队,"Yeah we got it"给红队。
- 监控:除了连接时间之类的,我们还需要"last activity time"之类的信息。要将它正确地放入 JSF 数据表中,CDI bean 中的 list/set 是最佳选择
我遇到了一些tutorial/detail,其中@ServerEndPoint 被注释为@ApplicationScoped。按照设计(一个服务器端点 = 一个 websocket 连接),我对此感到不舒服,所以我拒绝实施此解决方案
@Named @ApplicationScoped public class TickSessionHandler{ // There is no need to have a static Set, worst, // containers don't like it public Set<Session> SESSIONS; // let's initialize the set public TickSessionHandler{ this.SESSIONS = new HashSet<>(); } // ---------- sessions management public void addSession(Session session){ this.SESSIONS.add(session); } public void removeSession(Sesssion session){ this.SESSIONS.remove(session); } // ---------- Listen to the timer public void onTick(@Observes TickEvent event){ // if required, get the event attribute // and proceed // your request: this.SESSIONS.forEach(session -> { session.getBasicRemote().sendText("hello"); }); } }
希望对您有所帮助。解释看起来很长,但核心实现其实很轻。据我所知,这种要求没有捷径。