我正在尝试在zmq上实现“文件调度程序”(实际上是jeromq,我宁愿避免使用jni)。
我需要的是将传入文件加载到处理器:
理想情况下,我想要像https://github.com/zeromq/filemq但
这样的东西我的想法是使用taskvent / tasksink和asyncsrv样本的混合。
客户方:
服务器端:
我的第一个问题是:这看起来是正确的方法吗?还有什么更简单的吗?
我的第二个问题是:我当前的实现在发送实际文件数据时停滞不前。
这不是套接字充满和丢失数据的问题,我从一次性发送的非常小的文件开始。
有什么见解?
谢谢!
==================
按照raffian的建议,我简化了我的代码,移除了推/拉额外插座(现在你说它确实有意义)
我留下了“不工作”的插座!
这是我目前的代码。它有许多缺陷现在超出范围(客户端ID,下一个块等)。
现在,我只是想让两个人大致按照那个顺序互相交谈
服务器
object FileDispatcher extends App
{
val context = ZMQ.context(1)
// server is the frontend that pushes filenames to clients and receives requests
val server = context.socket(ZMQ.ROUTER)
server.bind("tcp://*:5565")
// backend handles clients requests
val backend = context.socket(ZMQ.DEALER)
backend.bind("inproc://backend")
// files to dispatch given in arguments
args.toList.foreach { filepath =>
println(s"publish $filepath")
server.send("newfile".getBytes(), ZMQ.SNDMORE)
server.send(filepath.getBytes(), 0)
}
// multithreaded server: router hands out requests to DEALER workers via a inproc queue
val NB_WORKERS = 1
val workers = List.fill(NB_WORKERS)(new Thread(new ServerWorker(context)))
workers foreach (_.start)
ZMQ.proxy(server, backend, null)
}
class ServerWorker(ctx: ZMQ.Context) extends Runnable
{
override def run()
{
val worker = ctx.socket(ZMQ.DEALER)
worker.connect("inproc://backend")
while (true)
{
val zmsg = ZMsg.recvMsg(worker)
zmsg.pop // drop inner queue envelope (?)
val cmd = zmsg.pop //cmd is used to continue/stop
cmd.toString match {
case "get" =>
val file = zmsg.pop.toString
println(s"clientReq: cmd: $cmd , file:$file")
//1- brute force: ignore cmd and send full file in one go!
worker.send("eof".getBytes, ZMQ.SNDMORE) //header indicates this is the last chunk
val bytes = io.Source.fromFile(file).mkString("").getBytes //dirty read, for testing only!
worker.send(bytes, 0)
println(s"${bytes.size} bytes sent for $file: "+new String(bytes))
case x => println("cmd "+x+" not implemented!")
}
}
}
}
客户端
object FileHandler extends App
{
val context = ZMQ.context(1)
// client is notified of new files then fetches file from server
val client = context.socket(ZMQ.DEALER)
client.connect("tcp://*:5565")
val poller = new ZMQ.Poller(1) //"poll" responses
poller.register(client, ZMQ.Poller.POLLIN)
while (true)
{
poller.poll
val zmsg = ZMsg.recvMsg(client)
val cmd = zmsg.pop
val data = zmsg.pop
// header is the command/action
cmd.toString match {
case "newfile" => startDownload(data.toString)// message content is the filename to fetch
case "chunk" => gotChunk(data.toString, zmsg.pop.getData) //filename, chunk
case "eof" => endDownload(data.toString, zmsg.pop.getData) //filename, last chunk
}
}
def startDownload(filename: String)
{
println("got notification: start download for "+filename)
client.send("get".getBytes, ZMQ.SNDMORE) //command header
client.send(filename.getBytes, 0)
}
def gotChunk(filename: String, bytes: Array[Byte])
{
println("got chunk for "+filename+": "+new String(bytes)) //callback the user here
client.send("next".getBytes, ZMQ.SNDMORE)
client.send(filename.getBytes, 0)
}
def endDownload(filename: String, bytes: Array[Byte])
{
println("got eof for "+filename+": "+new String(bytes)) //callback the user here
}
}
答案 0 :(得分:3)
在客户端上,PULL
DEALER
不需要PUSH
。
经销商PULL
和PUSH
合并,因此仅使用经销商,您的代码会更简单。
服务器也是如此,除非你正在做一些特别的事情,你不需要ROUTER
args.toList.foreach
,路由器是双向的。
服务器工作者获取请求,并将响应写回 inproc队列,但响应似乎永远不会离开服务器 (无法在wireshark中看到它)并且客户端卡在poller.poll上 等待回应。
在服务器中,您在启动代理之前使用ZMQProxy(..)
调度文件,这可能就是没有任何东西离开服务器的原因。首先启动代理,然后使用它;此外,一旦调用ZMQ.Poller items = new ZMQ.Poller (1);
items.register(receiver, ZMQ.Poller.POLLIN);
while (true) {
items.poll(TIMEOUT);
if (items.pollin(0)) {
message = receiver.recv(0);
,代码将无限期地阻塞,因此您需要一个单独的线程来发送文件路径。
客户端可能与轮询器有问题。轮询的典型模式是:
receiver.recv(0)
在上面的代码中,1)轮询直到超时,2)然后检查消息,如果可用,3)得到recv()
。但是在您的代码中,您进行了轮询,然后在不进行检查的情况下进入recv()
。在调用{{1}}之前,您需要检查轮询器是否有该轮询套接字的消息,否则,如果没有消息,接收器将挂起。