On Fri, 04 Jan 2013 13:20:45 GMT, zhaopuming wrote:

Our use case senario is like this:

  1. We have a server that receives browser requests.

  2. For each browser request, we have to send them to mulitple remote servers (about 10~20 of them), by using HttpClient

  3. When all of the remote servers sends a reply back, or a timeout occurs, we need to put together the replies we get so far, and do some computation on them.

  4. a response is generated by the computation and send back to the browser.

In step 3, if the timeout occurs before all replies are sent back, those that are not back should be ignored, or better aborted.

The remote servers are fairly stable, so we'd like to make an HttpClient for each of them.

For each browser request, all HttpClients are used to send a client request.

But looking at httpClient sample code:

auto res = requestHttp("http://google.com", (req){/* could e.g. add headers here before sending*/});
logInfo("Response: %s", res.bodyReader.readAllUtf8());

I have no idea of how I could write the logic of 'Whoever comes first will be served and those later than the timeout will be aborted'.

Could you shed some light on that, Sönke?

Thanks :-)

One way to achieve this would be to use a timer and and a signal. The requests are all run in parallel in different tasks/fibers using runTask and the original task will wait until either the timeout is reached or all requests have been answered. The only drawback is that all the late requests will continue to run after the timeout - it shouldn't have any serious impact on anything though, their connections will time out eventually or the request is finished a bit later.

It's planned for later to have more facilities for controlling tasks (e.g. waiting for their end or terminating them) and also to have a generic broadcast class that could be made general enough to handle this situation. I think such a broadcast class is quite important because generally it shouldn't be necessary to work with such low-level details such as rawYield. But I cannot really say when I'm able to get that done...

Btw. using requestHttp() will keep a connection open to each server automatically using a ConnectionPool internally. So there is no need to explicitly store HttpClient instances.

string[] servers;

void handleRequest(HttpServerRequest req, HttpServerResponse res)
{
	// if the body is JSON and JSON parsing is enabled
	// in the HttpServerSettings, this will need to get
	// req.json.toString() instead.
	auto body = req.bodyReader.readAll();

	// run all broadcast requests as separate tasks
	string[string] replies;
	auto tm = setTimer(dur!"seconds"(10), null);
	auto sig = createSignal();
	foreach( srv; servers )
		runTask({
			auto cres = requestHttp("http://"~srv~"/url", (creq){
				creq.method = req.method;
				creq.bodyWriter.write(body);
			});
			replies[srv] = cres.bodyReader.readAllUtf8();

			// wake up the original fiber
			sig.emit();
		});

	// yield until either the timeout is reached or all replies are collected.
	// the timer and the signal will both cause rawYield() to continue.
	tm.acquire();
	while( tm.pending || replies.length < servers.length )
		rawYield();
	tm.release();
	
	// save the current replies (other requests might still come in later)
	auto saved_replies = replies;
	replies = null;

	// do something with saved_replies and respond to the original request...
}

Please bear with me, I haven't tested the code, so it may very well contain some mistakes.

Regards