HAPI - 如何正确停止 SimpleServer 并防止进一步连接

HAPI - How to stop SimpleServer correctly and prevent further connections

我正在构建一个应用程序,该应用程序具有由 CommunicationProcess class 管理的多个服务器和客户端 HL7 连接。该应用程序的部分功能是在添加新连接时重新启动该进程。客户端连接不会造成问题,因为一旦客户端停止,服务器端就无法重新连接。然而,对于服务器连接,我似乎立即从(相当激进的)客户端重新连接。这是我必须停止服务器连接的代码:

public void disconnect() 
{
    usageServer.getRemoteConnections().forEach((connection) -> connection.close());
    usageServer.stopAndWait();
    usageServer.getRemoteConnections().forEach((connection) -> connection.close());   
}

public void stop()
{
    running.set(false);

    disconnect();
}

这是我对 connectionReceived 的实现:

@Override
public void connectionReceived(Connection theC) 
{
    if (running.get())
    {
        setStatus(ConnectionStatus.CONNECTED);
    }
    else
    {
        theC.close();
    }
}

如您所见,想法是在从 CommunicationProcess class 接收到停止信号时将全局 AtomicBoolean 设置为 false,这会拒绝任何新连接,并停止服务器。不知何故,这仍然允许客户端在此过程中保持连接。客户端是一个应用程序,我不能说出它的名字,但它已经存在了十多年,我知道它不会成为问题,因为多年来我一直在支持它作为我日常工作的一部分它根本不像那样。

知道为什么我的代码实际上没有终止连接吗?我觉得我已经探索了很多 API 并且我没有找到取消注册可能会解决此问题的连接侦听器的方法。此外,我看不到扩展这些服务器 classes 的方法,因为一切都被相当凶猛地封装和私有化了。

谢谢

我正在审查 HAPI 库的代码。

您描述的行为的原因可能如下。

当服务器启动时,它们会创建一个名为 AcceptorThread 的组件。顾名思义,该线程的职责是初始化 ServerSocket 用于接收传入的客户端连接,并接受它们。

这个线程,作为 API 提出的每个 Service 抽象,在这样的循环中运行:

/**
  * Runs the thread.
  * 
  * @see java.lang.Runnable#run()
  */
public final void run() {
  try {
    afterStartup();
    log.debug("Thread {} entering main loop", name);
    while (isRunning()) {
      handle();
      startupLatch.countDown();
    }
    log.debug("Thread {} leaving main loop", name);
  } catch (RuntimeException t) {
    if (t.getCause() != null) {
      serviceExitedWithException = t.getCause();
    } else {
      serviceExitedWithException = t;
    }
    log.warn("Thread exiting main loop due to exception:", t);
  } catch (Throwable t) {
    serviceExitedWithException = t;
    log.warn("Thread exiting main loop due to exception:", t);
  } finally {
    startupLatch.countDown();
    afterTermination();
  }

}

当您在服务器中调用方法 stopAndWait 时,它也会尝试停止该线程。

停止过程基本上更改了 boolean 标志,该标志控制组件是否``ìsRunning()```。

如你所见,虽然它设置了flag为false,但循环中方法handle的调用仍然必须结束。

这是AcceptorThreadhandle方法的实现:

@Override
protected void handle() {
  try {
    Socket s = ss.accept();
    socketFactory.configureNewAcceptedSocket(s);
    if (!queue.offer(new AcceptedSocket(s))) {
      log.error("Denied enqueuing server-side socket {}", s);
      s.close();
    } else
      log.debug("Enqueued server-side socket {}", s);
  } catch (SocketTimeoutException e) { /* OK - just timed out */
    log.trace("No connection established while waiting");
  } catch (IOException e) {
    log.error("Error while accepting connections", e);
  }
}

如您所见,该方法调用 ServerSocket.accept,从而允许新的传入连接。

为了断开这个服务器端套接字,我们可以从另一个线程调用close

其实这个过程就是AcceptorTreadafterTermination方法实现的:

@Override
protected void afterTermination() {
  try {
    if (ss != null && !ss.isClosed())
      ss.close();
  } catch (IOException e) {
    log.warn("Error during stopping the thread", e);
  }
}

不幸的是 - 你是对的,API 非常接近! - 没有明确的方法来做到这一点。

一个可能的解决方案是实现您自己的 HL7Service,将其命名为 MySimpleServer,使用 SimpleServer 的代码作为基线,并仅更改方法的实现 afterTermination:

/**
  * Close down socket
  */
@Override
protected void afterTermination() {
  super.afterTermination();
  // Terminate server side socket
  acceptor.afterTermination();
  // Terminate the acceptor thread itself
  acceptor.close();
}

请注意:我们调用 acceptor.afterTermination() 来直接关闭底层服务器端套接字,而不是调用 acceptor.stop()

为了避免AcceptorThread中的handle方法引发的错误,我们也可以从原来的class实现一个新的class,或者只是尝试覆盖[=22] =] 如果服务器端套接字关闭,要考虑的方法:

@Override
protected void handle() {
  try {
    if (ss.isClosed()) {
      log.debug("The server-side socket is closed. No new connections will be allowed.");
      return;
    }

    Socket s = ss.accept();
    socketFactory.configureNewAcceptedSocket(s);
    if (!queue.offer(new AcceptedSocket(s))) {
      log.error("Denied enqueuing server-side socket {}", s);
      s.close();
    } else
      log.debug("Enqueued server-side socket {}", s);
  } catch (SocketTimeoutException e) { /* OK - just timed out */
    log.trace("No connection established while waiting");
  } catch (IOException e) {
    log.error("Error while accepting connections", e);
  }
}

为了测试,您可以尝试这样的操作:

public static void main(String[] args) throws Exception {

  HapiContext ctx = new DefaultHapiContext();

  HL7Service server = new MySimpleServer(8888);
  server.startAndWait();

  Connection client1 = ctx.newClient("127.0.0.1", 8888, false);

  server.getRemoteConnections().forEach((connection) -> connection.close());

  server.stopAndWait();

  try {
    Connection client2 = ctx.newClient("127.0.0.1", 8888, false);
  } catch (Throwable t) {
    t.printStackTrace();
  }

  ctx.close();

  System.exit(0);
}