我使用jeromq 0.3.2编写了一个用Java编写的多线程应用程序。我正在努力将它放入Tanuki服务包装器中,以便它作为Windows服务运行,而我在干净地停止线程时遇到了一些麻烦。代理的run()
方法是来自Simple Pirate的the guide模式的变体,如下所示:
public void run()
{
ZContext context = new ZContext();
Socket frontend = context.createSocket(ZMQ.ROUTER);
Socket backend = context.createSocket(ZMQ.ROUTER);
frontend.bind("tcp://*:5555");
backend.bind("inproc://workers");
while (!Thread.currentThread().isInterrupted())
{
ZMQ.Poller poller = new ZMQ.Poller(2);
poller.register(frontend, ZMQ.Poller.POLLIN);
poller.register(backend, ZMQ.Poller.POLLIN);
poller.poll();
if (poller.pollin(0))
{
processFrontend(frontend, backend, context);
}
if (poller.pollin(1))
{
processBackend(frontend, backend);
}
}
// additonal code to close worker threads
}
当控制包装器想要停止应用程序时,如何干净地退出此循环?
如果当前没有连接的客户端,则在调用poller.poll()
时阻止循环,因此如果包装器在线程上调用interrupt()
,则忽略它。如果有客户端当前正在发送消息,则调用interrupt()
会导致poller.poll()
方法抛出zmq.ZError$IOException: java.nio.channels.ClosedByInterruptException
我也尝试过使用:
PollItem[] items = {
new PollItem(frontend, Poller.POLLIN),
new PollItem(backend, Poller.POLLIN)
};
int rc = ZMQ.poll(items, 2, -1);
if (rc == -1)
break;
if (items[0].isReadable())
{
processFrontend(frontend, backend, context);
}
if (items[1].isReadable())
{
processBackend(frontend, backend);
}
但是对ZMQ.poll
的调用表现出相同的行为。两种可能的替代方案是:
ZMQ.poll
上设置超时,并在tryEx / catch中包含run()
方法的内容以用于IOException。 processFrontend
中读取并导致代码突破循环。第一个似乎有点脏,第二个感觉有点脆弱。我还应该考虑其他方法吗?是否有一些我可以使用的轮询方法能更干净地对线程中断作出反应?
答案 0 :(得分:2)
最好将循环提取到单独的线程并打开另一个inproc PULL
套接字,以便循环轮询来自3个套接字的消息:frontend
,backend
和{{1 }}。如果主线程收到中断信号,则应该向代理循环发送control
命令。
这个模式正好在我的项目中实现:https://github.com/thriftzmq/thriftzmq-java/blob/master/thriftzmq/src/main/java/org/thriftzmq/ProxyLoop.java
在主线程上,您可以添加STOP
并从其中发送shutdownHook
命令到循环。
STOP
P.S。使用guava-library中的原语来管理服务生命周期使生活变得更加容易。