想给 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 解决了它:

  1. 调度器class:发送事件的时钟
  2. Websocket 端点:基本的 websocket 会话处理程序
  3. Websocket 会话处理程序:捕获事件并管理所有 websocket 会话的指挥者

带代码的长版本

现在是代码,1) 是 Mike 在评论中提到的代码:一个 classic Sc​​heduler,它触发了一些事件。 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,因此它始终可用。我使用它的原因:

  1. 触发 TickEvent 时,您不知道 TickEndPoint 实例是否存在,因此事件捕获可能会引发一些愚蠢的错误。我们用 @Observes(notifyObserver = Reception.IF_EXISTS) 修复了它,但我们遇到了其他 EJB 问题,我将在 2.
  2. 中详细介绍
  3. 由于我尚未确定的原因,当我在我的@ServerEndPoint 中使用@Observes 时,我所有的@EJB 注入都失败了,所以我将@EJB 注入切换为@Inject。但是由于 ApplicationScoped 是一个 CDI bean,@EJB 和@Observes 在会话处理程序中可以很好地协同工作 class。我们并不真正需要 @EJB 注释本身,但为了与其他 bean 的设计一致性,我们希望我们所有的 EJB 都使用 @EJB 进行注释。
  4. 监控:您可以进行监控,例如您在时间 T 发送了多少 "hello",给您一个时间与连接会话数的图表。

另外,在我们的特殊情况下,我们有一些要点,您稍后可能会遇到。

  1. 我们需要 select SESSIONS 的特定子集,而不是全部。在 @ServerEndPoint 中设置一个静态 Set 会导致某些东西将我变成绿巨人行为,尤其是当您具有 EJB 依赖项时。在一个游戏示例中,假设您有蓝队和红队夺旗场景。如果红队夺旗,需要发送"Oops, we lost the flag"给蓝队,"Yeah we got it"给红队。
  2. 监控:除了连接时间之类的,我们还需要"last activity time"之类的信息。要将它正确地放入 JSF 数据表中,CDI bean 中的 list/set 是最佳选择
  3. 我遇到了一些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");
            });
        }
    
    }
    

希望对您有所帮助。解释看起来很长,但核心实现其实很轻。据我所知,这种要求没有捷径。