Implementing a non-blocking delay between request and response

Marcus Clyne ngx.eugaia at gmail.com
Fri Jan 15 03:55:24 MSK 2010


Hi,

Tronman wrote:
> Hi there,
>
> I'm trying to figure out how to implement a non blocking delay between when a request comes into a module, and when it is responded too.
>
> For example, consider a blocking delay:
>
>
> static ngx_int_t
> ngx_http_my_module_handler(ngx_http_request_t *r)
> {
> 	r->headers_out.status = NGX_HTTP_OK
> 	r->header_only = 1;
> 	r->headers_out.content_length_n = 0;
>
> 	ngx_sleep(10);
>
> 	rc = ngx_http_send_header(r);	
> 	ngx_http_finalize_request(r, rc);
> 	return NGX_DONE;
> }
>
>
> Which would allow a client to connect, wait for 10 seconds, and then send the response. That's the behaviour I want, but such a wait as implemented here is blocking, that is, while the first request is waiting to be served, all the other requests are blocked until it completed. I want to make it non blocking so that while one is being processed (and waiting), others can come into the system and be handled (which may themselves wait, or may not).
>
> So after some research, I found out about nginx timers, and it seems I should be able to set a timer/event hander to implement the wait and be non-blocking:
>
>
> static void ngx_http_my_module_delay_handler(ngx_event_t *ev);
>
> static ngx_int_t
> ngx_http_my_module_handler(ngx_http_request_t *r)
> {
> 	r->headers_out.status = NGX_HTTP_OK
> 	r->header_only = 1;
> 	r->headers_out.content_length_n = 0;
>
> 	ngx_event_t *wakeupFromSleep;
> 	wakeupFromSleep = ngx_pcalloc(r->pool, sizeof(ngx_event_t));
>   
You should use ngx_add_event () here instead of allocating the space for 
the event struct directly.
> 	if (wakeupFromSleep == NULL)
> 		ngx_log_stderr(0, "Wakeupfromsleep null");
>   
As a side-note here, this should have included returning a failure - you 
would have got a segfault on failure otherwise

if (wakeupFromSleep == NULL) {
	ngx_log_stderr(0, "Wakeupfromsleep null");
	return NGX_ERROR;
}


> 	wakeupFromSleep->handler = ngx_http_my_module_delay_handler;
> 	wakeupFromSleep->data = r;
> 	ngx_log_stderr(0, "Sleeping for 30 seconds");
> 	ngx_add_timer(wakeupFromSleep, 30);
>   
If you do nothing else here, then your request will return immediately.  
(Check out the code in ngx_http_finalize_request() to understand why.) 
Adding

r->count++;

is one way that should prevent it from returning the first time.
> 	return NGX_DONE;
> }
>
> static void
> ngx_http_my_module_delay_handler(ngx_event_t *ev)
> {
> 	int rc;
> 	ngx_http_request_t *r = ev->data;
> 	ngx_log_stderr(0, "Done waiting. Sending response.");
>
> 	rc = ngx_http_send_header(r);	
> 	ngx_http_finalize_request(r, rc);
> 	ngx_del_timer(ev);
>   
You should add a ngx_del_event() too here.  I'm not sure whether you 
should delete the event and timer before finalizing the request (I don't 
think so), but it may be required to avoid segfaulting.
> }
By the way, this question is probably better asked in the nginx-devel 
mailing list rather than the main one.


Hope that helps,

Marcus.



More information about the nginx mailing list