//------------------------------------------------------------------------------ //if worker already present remove it and re-insert it in the right //position void push(Workers& workers, int id) { Workers::iterator it = std::find_if(workers.begin(), workers.end(), [id](const worker_info& wi){ return wi.id() == id; }); if(it != workers.end()) workers.erase(it); workers.insert(worker_info(id)); }
//------------------------------------------------------------------------------ //elements are ordered from highest to lowest //1) find the first element which has a time > expiration time //2) remove all elements from that element to last element is set void purge(Workers& workers, const duration& cutoff) { typedef Workers::iterator WI; WI start = std::find_if( workers.begin(), workers.end(), [&cutoff](const worker_info& wi) { return std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - wi.timestamp() ) > cutoff; }); if(start == workers.end()) return; workers.erase(start, workers.end()); }
//------------------------------------------------------------------------------ int main(int argc, char** argv) { if(argc < 3) { std::cout << "usage: " << argv[0] << " <frontend address> <backend address>" << std::endl; return 0; } const char* FRONTEND_URI = argv[1]; const char* BACKEND_URI = argv[2]; const int MAX_REQUESTS = 100; //create communication objects void* context = zmq_ctx_new(); assert(context); void* frontend = zmq_socket(context, ZMQ_ROUTER); assert(frontend); void* backend = zmq_socket(context, ZMQ_ROUTER); assert(backend); assert(zmq_bind(frontend, FRONTEND_URI) == 0); assert(zmq_bind(backend, BACKEND_URI) == 0); //workers ordered queue Workers workers; int worker_id = -1; int client_id = -1; int rc = -1; std::vector< char > request(0x100, 0); std::vector< char > reply(0x100, 0); int serviced_requests = 0; //loop until max requests servided while(serviced_requests < MAX_REQUESTS) { zmq_pollitem_t items[] = { {backend, 0, ZMQ_POLLIN, 0}, {frontend, 0, ZMQ_POLLIN, 0}}; //remove all workers that have not been active for a //time > expiration interval //XXX TODO: TEST purge(workers, EXPIRATION_INTERVAL); //XXX //poll for incoming requests: if no workers are available //only poll for workers(backend) since there is no point //in trying to service a client request without active //workers rc = zmq_poll(items, workers.size() > 0 ? 2 : 1, TIMEOUT); if(rc == -1) break; //data from workers if(items[0].revents & ZMQ_POLLIN) { assert(zmq_recv(backend, &worker_id, sizeof(worker_id), 0) > 0); assert(zmq_recv(backend, 0, 0, 0) == 0); assert(zmq_recv(backend, &client_id, sizeof(client_id), 0) > 0); //add worker to list of available workers push(workers, worker_id); assert(workers.size() > 0); //of not a 'ready' message forward message to frontend //workers send 'ready' messages when either if(client_id != WORKER_READY) { int seq_id = -1; assert(zmq_recv(backend, 0, 0, 0) == 0); rc = zmq_recv(backend, &seq_id, sizeof(seq_id), 0); assert(rc > 0); rc = zmq_recv(backend, &reply[0], reply.size(), 0); assert(rc > 0); zmq_send(frontend, &client_id, sizeof(client_id), ZMQ_SNDMORE); zmq_send(frontend, 0, 0, ZMQ_SNDMORE); zmq_send(frontend, &seq_id, sizeof(seq_id), ZMQ_SNDMORE); zmq_send(frontend, &reply[0], rc, 0); ++serviced_requests; } } //request from clients if(items[1].revents & ZMQ_POLLIN) { int seq_id = -1; //receive request |client id|<null>|request id|data| zmq_recv(frontend, &client_id, sizeof(client_id), 0); zmq_recv(frontend, 0, 0, 0); rc = zmq_recv(frontend, &seq_id, sizeof(seq_id), 0); assert(rc > 0); const int req_size = zmq_recv(frontend, &request[0], request.size(), 0); assert(req_size > 0); //take worker from list and forward request to it worker_id = pop(workers); assert(worker_id > 0); zmq_send(backend, &worker_id, sizeof(worker_id), ZMQ_SNDMORE); zmq_send(backend, 0, 0, ZMQ_SNDMORE); zmq_send(backend, &client_id, sizeof(client_id), ZMQ_SNDMORE); zmq_send(backend, 0, 0, ZMQ_SNDMORE); zmq_send(backend, &seq_id, sizeof(seq_id), ZMQ_SNDMORE); zmq_send(backend, &request[0], req_size, 0); } const int hb = HEARTBEAT; //capturing HEARTBEAT directly generates //a warning because the lambda function should //not capture a variable with non-automatic //storage //send heartbeat request to all workers: workers reply to such request //with a 'ready' message std::for_each(workers.begin(), workers.end(), [backend, hb](const worker_info& wi) { const int id = wi.id(); zmq_send(backend, &id, sizeof(id), ZMQ_SNDMORE); zmq_send(backend, 0, 0, ZMQ_SNDMORE); zmq_send(backend, &hb, sizeof(hb), 0); }); } zmq_close(frontend); zmq_close(backend); zmq_ctx_destroy(context); return 0; }