void NetworkServer::serve(){ writer = new ProcWorkerPool("writer"); writer->start(num_writers); reader = new ProcWorkerPool("reader"); reader->start(num_readers); pthread_t tid; int err = pthread_create(&tid, NULL, &NetworkServer::_ops_timer_thread, this); if (err != 0) { log_error("can't start ops timer thread: %s", strerror(err)); exit(-1); } link_dict_t ready_dict; link_dict_t tmp_dict; link_dict_t blocked_dict; link_dict_t::iterator it; const Fdevents::events_t *events; fdes->set(serv_link->fd(), FDEVENT_IN, 0, serv_link); fdes->set(this->reader->fd(), FDEVENT_IN, 0, this->reader); fdes->set(this->writer->fd(), FDEVENT_IN, 0, this->writer); uint32_t last_ticks = g_ticks; while(!quit){ // status report if((uint32_t)(g_ticks - last_ticks) >= STATUS_REPORT_TICKS){ last_ticks = g_ticks; log_debug("server running, links: %d", this->link_count); } ready_dict.swap(tmp_dict); tmp_dict.clear(); if(!ready_dict.empty()){ /* ready_dict not empty, so we should return immediately */ events = fdes->wait(0); }else{ events = fdes->wait(50); } if(events == NULL){ log_fatal("events.wait error: %s", strerror(errno)); break; } for(int i=0; i<(int)events->size(); i++){ const Fdevent *fde = events->at(i); if(fde->data.ptr == serv_link){ Link *link = accept_link(); if(link){ this->link_count ++; log_debug("new link from %s:%d, fd: %d, links: %d", link->remote_ip, link->remote_port, link->fd(), this->link_count); fdes->set(link->fd(), FDEVENT_IN, 1, link); } }else if(fde->data.ptr == this->reader || fde->data.ptr == this->writer){ ProcWorkerPool *worker = (ProcWorkerPool *)fde->data.ptr; ProcJob job; if(worker->pop(&job) == 0){ log_fatal("reading result from workers error!"); exit(0); } if(proc_result(&job, &ready_dict) == PROC_ERROR){ // } }else{ proc_client_event(fde, &ready_dict); } } /* if clients paused, add specified link into blocked_list and disable parsing request */ if(NetworkServer::clients_paused) { if(NetworkServer::clients_pause_end_time < time_ms()) { NetworkServer::clients_paused = 0; NetworkServer::clients_pause_end_time = 0; ready_dict.insert(blocked_dict.begin(), blocked_dict.end()); blocked_dict.clear(); } else { blocked_dict.insert(ready_dict.begin(), ready_dict.end()); ready_dict.clear(); continue; } } for(it = ready_dict.begin(); it != ready_dict.end(); it ++){ Link *link = it->second; if(link->error()){ this->link_count --; fdes->del(link->fd()); delete link; continue; } const Request *req = link->recv(); if(req == NULL){ log_warn("fd: %d, link parse error, delete link", link->fd()); this->link_count --; fdes->del(link->fd()); delete link; continue; } if(req->empty()){ fdes->set(link->fd(), FDEVENT_IN, 1, link); continue; } link->active_time = millitime(); ProcJob job; job.link = link; this->proc(&job); if(job.result == PROC_THREAD){ fdes->del(link->fd()); continue; } if(job.result == PROC_BACKEND){ fdes->del(link->fd()); this->link_count --; continue; } if(proc_result(&job, &tmp_dict) == PROC_ERROR){ // } } // end foreach ready link } }
int main(int argc, char **argv) { long long begin_time = millitime(); char **argptr = argv+1; if (!*argptr) usage(); std::string storename = *argptr++; if (!*argptr) usage(); int uid = atoi(*argptr++); set_log_prefix(string_printf("%d %d ", getpid(), uid)); if (!*argptr) usage(); std::string full_channel_name = *argptr++; #if FFT_SUPPORT bool writing_fft = false; size_t fftpos = full_channel_name.rfind(".DFT"); if (fftpos != std::string::npos) { full_channel_name = full_channel_name.substr(0, fftpos); writing_fft = true; } #endif /* FFT_SUPPORT */ if (!*argptr) usage(); int tile_level = atoi(*argptr++); if (!*argptr) usage(); long long tile_offset = atoll(*argptr++); if (*argptr) usage(); // Desired level and offset // Translation between tile request and tilestore: // tile: level 0 is 512 samples in 512 seconds // store: level 0 is 65536 samples in 1 second // for tile level 0, we want to get store level 14, which is 65536 samples in 16384 seconds // Levels differ by 9 between client and server TileIndex client_tile_index = TileIndex(tile_level+9, tile_offset); { std::string arglist; for (int i = 0; i < argc; i++) { if (i) arglist += " "; arglist += std::string("'")+argv[i]+"'"; } log_f("gettile START: %s (time %.9f-%.9f)", arglist.c_str(), client_tile_index.start_time(), client_tile_index.end_time()); } FilesystemKVS store(storename.c_str()); // 5th ancestor TileIndex requested_index = client_tile_index.parent().parent().parent().parent().parent(); std::vector<DataSample<double> > double_samples; std::vector<DataSample<std::string> > string_samples; std::vector<DataSample<std::string> > comments; bool doubles_binned, strings_binned, comments_binned; // TODO: If writing FFT, ***get more data*** // TODO: Use min_time_required and max_time_required, get max-res data read_tile_samples(store, uid, full_channel_name, requested_index, client_tile_index, double_samples, doubles_binned); #if FFT_SUPPORT if (writing_fft) { std::vector<std::vector<double> > fft, shifted; int num_values; windowed_fft(double_samples, requested_index, fft); present_fft(fft, shifted, num_values); // JSON tile to send back to the client includes some of the same // information as a non-DFT tile Json::Value tile(Json::objectValue); tile["level"] = Json::Value(tile_level); // See discussion below for reason to cast tile_offset // from long long to double tile["offset"] = Json::Value((double)tile_offset); tile["num_values"] = Json::Value(num_values); tile["dft"] = Json::Value(Json::arrayValue); for (unsigned window_id = 0; window_id < shifted.size(); window_id++) { Json::Value window(Json::arrayValue); for (unsigned i = 0; i < shifted[window_id].size(); i++) window.append(shifted[window_id][i]); tile["dft"].append(window); } std::cout << Json::FastWriter().write(tile) << std::endl; return 0; } #endif /* FFT_SUPPORT */ read_tile_samples(store, uid, full_channel_name, requested_index, client_tile_index, string_samples, strings_binned); read_tile_samples(store, uid, full_channel_name+"._comment", requested_index, client_tile_index, comments, comments_binned); string_samples.insert(string_samples.end(), comments.begin(), comments.end()); std::sort(string_samples.begin(), string_samples.end(), DataSample<std::string>::time_lessthan); std::map<double, DataSample<double> > double_sample_map; for (unsigned i = 0; i < double_samples.size(); i++) { double_sample_map[double_samples[i].time] = double_samples[i]; // TODO: combine if two samples at same time? } std::set<double> has_string; for (unsigned i = 0; i < string_samples.size(); i++) { has_string.insert(string_samples[i].time); } std::vector<GraphSample> graph_samples; bool has_fifth_col = string_samples.size()>0; for (unsigned i = 0; i < string_samples.size(); i++) { if (double_sample_map.find(string_samples[i].time) != double_sample_map.end()) { GraphSample gs(double_sample_map[string_samples[i].time]); gs.has_comment = true; gs.comment = string_samples[i].value; graph_samples.push_back(gs); } else { graph_samples.push_back(GraphSample(string_samples[i])); } } for (unsigned i = 0; i < double_samples.size(); i++) { if (has_string.find(double_samples[i].time) == has_string.end()) { graph_samples.push_back(GraphSample(double_samples[i])); } } std::sort(graph_samples.begin(), graph_samples.end()); double bin_width = client_tile_index.duration() / 512.0; double line_break_threshold = bin_width * 4.0; if (!doubles_binned && double_samples.size() > 1) { // Find the median distance between samples std::vector<double> spacing(double_samples.size()-1); for (size_t i = 0; i < double_samples.size()-1; i++) { spacing[i] = double_samples[i+1].time - double_samples[i].time; } std::sort(spacing.begin(), spacing.end()); double median_spacing = spacing[spacing.size()/2]; // Set line_break_threshold to larger of 4*median_spacing and 4*bin_width line_break_threshold = std::max(line_break_threshold, median_spacing * 4); } if (graph_samples.size()) { log_f("gettile: outputting %zd samples", graph_samples.size()); Json::Value tile(Json::objectValue); tile["level"] = Json::Value(tile_level); // An aside about offset type and precision: // JSONCPP doesn't have a long long type; to preserve full resolution we need to convert to double here. As Javascript itself // will read this as a double-precision value, we're not introducing a problem. // For a detailed discussion, see https://sites.google.com/a/bodytrack.org/wiki/website/tile-coordinates-and-numeric-precision // Irritatingly, JSONCPP wants to add ".0" to the end of floating-point numbers that don't need it. This is inconsistent // with Javascript itself and simply introduces extra bytes to the representation tile["offset"] = Json::Value((double)tile_offset); tile["fields"] = Json::Value(Json::arrayValue); tile["fields"].append(Json::Value("time")); tile["fields"].append(Json::Value("mean")); tile["fields"].append(Json::Value("stddev")); tile["fields"].append(Json::Value("count")); if (has_fifth_col) tile["fields"].append(Json::Value("comment")); Json::Value data(Json::arrayValue); double previous_sample_time = client_tile_index.start_time(); bool previous_had_value = true; for (unsigned i = 0; i < graph_samples.size(); i++) { // TODO: improve linebreak calculations: // 1) observe channel specs line break size from database (expressed in time; some observations have long time periods and others short) // 2) insert breaks at beginning or end of tile if needed // 3) should client be the one to decide where line breaks are (if we give it the threshold?) if (graph_samples[i].time - previous_sample_time > line_break_threshold || !graph_samples[i].has_value || !previous_had_value) { // Insert line break, which has value -1e+308 Json::Value sample = Json::Value(Json::arrayValue); sample.append(Json::Value(0.5*(graph_samples[i].time+previous_sample_time))); sample.append(Json::Value(-1e308)); sample.append(Json::Value(0)); sample.append(Json::Value(0)); if (has_fifth_col) sample.append(Json::Value()); // NULL data.append(sample); } previous_sample_time = graph_samples[i].time; previous_had_value = graph_samples[i].has_value; { Json::Value sample = Json::Value(Json::arrayValue); sample.append(Json::Value(graph_samples[i].time)); sample.append(Json::Value(graph_samples[i].has_value ? graph_samples[i].value : 0.0)); // TODO: fix datastore so we never see NAN crop up here! sample.append(Json::Value(isnan(graph_samples[i].stddev) ? 0 : graph_samples[i].stddev)); sample.append(Json::Value(graph_samples[i].weight)); if (has_fifth_col) { sample.append(graph_samples[i].has_comment ? Json::Value(graph_samples[i].comment) : Json::Value()); } data.append(sample); } } if (client_tile_index.end_time() - previous_sample_time > line_break_threshold || !previous_had_value) { // Insert line break, which has value -1e+308 Json::Value sample = Json::Value(Json::arrayValue); sample.append(Json::Value(0.5*(previous_sample_time + client_tile_index.end_time()))); sample.append(Json::Value(-1e308)); sample.append(Json::Value(0)); sample.append(Json::Value(0)); if (has_fifth_col) sample.append(Json::Value()); // NULL data.append(sample); } tile["data"] = data; // only include the sample_width field if we actually binned if (doubles_binned) { tile["sample_width"] = bin_width; } printf("%s\n", rtrim(Json::FastWriter().write(tile)).c_str()); } else { log_f("gettile: no samples"); printf("{}"); } log_f("gettile: finished in %lld msec", millitime() - begin_time); return 0; }
void run(int argc, char **argv){ ready_list_t ready_list; ready_list_t ready_list_2; ready_list_t::iterator it; const Fdevents::events_t *events; Server serv(ssdb); Fdevents select; select.set(serv_link->fd(), FDEVENT_IN, 0, serv_link); select.set(serv.reader->fd(), FDEVENT_IN, 0, serv.reader); select.set(serv.writer->fd(), FDEVENT_IN, 0, serv.writer); int link_count = 0; while(!quit){ bool write_pending = false; ready_list.clear(); ready_list_2.clear(); if(write_pending || !ready_list.empty()){ // give links that are not in ready_list a chance events = select.wait(0); }else{ events = select.wait(50); } if(events == NULL){ log_fatal("events.wait error: %s", strerror(errno)); break; } for(int i=0; i<(int)events->size(); i++){ const Fdevent *fde = events->at(i); if(fde->data.ptr == serv_link){ Link *link = serv_link->accept(); if(link == NULL){ log_error("accept fail!"); continue; } link_count ++; log_info("new link from %s:%d, fd: %d, link_count: %d", link->remote_ip, link->remote_port, link->fd(), link_count); link->nodelay(); link->noblock(); link->create_time = millitime(); link->active_time = link->create_time; select.set(link->fd(), FDEVENT_IN, 1, link); }else if(fde->data.ptr == serv.reader || fde->data.ptr == serv.writer){ WorkerPool<Server::ProcWorker, ProcJob> *worker = (WorkerPool<Server::ProcWorker, ProcJob> *)fde->data.ptr; ProcJob job; if(worker->pop(&job) == 0){ log_fatal("reading result from workers error!"); exit(0); } if(proc_result(job, select, ready_list_2) == PROC_ERROR){ link_count --; } }else{ Link *link = (Link *)fde->data.ptr; // 不能同时监听读和写事件, 只能监听其中一个 if(fde->events & FDEVENT_ERR){ log_info("fd: %d error, delete link", link->fd()); link_count --; select.del(link->fd()); delete link; }else if(fde->events & FDEVENT_IN){ int len = link->read(); //log_trace("fd: %d read: %d", link->fd(), len); if(len <= 0){ log_info("fd: %d, read: %d, delete link", link->fd(), len); link_count --; select.del(link->fd()); delete link; }else{ ready_list.push_back(link); } }else if(fde->events & FDEVENT_OUT){ int len = link->write(); //log_trace("fd: %d write: %d", link->fd(), len); if(len <= 0){ log_info("fd: %d, write: %d, delete link", link->fd(), len); link_count --; select.del(link->fd()); delete link; }else if(link->output->empty()){ //log_trace("delete %d from select.out", link->fd()); select.clr(link->fd(), FDEVENT_OUT); if(!link->input->empty()){ ready_list.push_back(link); }else{ //log_trace("add %d to select.in", link->fd()); select.set(link->fd(), FDEVENT_IN, 1, link); } }else{ write_pending = true; } } } } for(it = ready_list.begin(); it != ready_list.end(); it ++){ Link *link = *it; const Request *req = link->recv(); if(req == NULL){ log_warn("fd: %d, link parse error, delete link", link->fd()); link_count --; select.del(link->fd()); delete link; continue; } if(req->empty()){ if(!select.isset(link->fd(), FDEVENT_IN)){ //log_trace("add %d to select.in", link->fd()); select.set(link->fd(), FDEVENT_IN, 1, link); } continue; } link->active_time = millitime(); ProcJob job; job.link = link; serv.proc(&job); if(job.result == PROC_THREAD){ select.del(link->fd()); continue; } if(job.result == PROC_BACKEND){ select.del(link->fd()); link_count --; continue; } if(proc_result(job, select, ready_list_2) == PROC_ERROR){ link_count --; } } // end foreach ready link ready_list.swap(ready_list_2); } }
u32_t sys_jiffies(void) { return millitime(); }