This has turned out to be a bit more complicated than we originally
thought.
This is what I tried:
version 1) using SocketChannel#register directly in my own thread:
channel.register(grizzlySelector, SelectionKey.OP_READ,
System.currentTimeMillis());
version 2) using SelectorHandler#addPendingIO:
boolean origVal = ((TCPSelectorHandler)
selectorHandler).isFinishIOUsingCurrentThread();
((TCPSelectorHandler)
selectorHandler).setFinishIOUsingCurrentThread(false);
selectorHandler.addPendingIO(
new Runnable() {
public void run() {
try {
channel.register(selectorHandler.getSelector(),
SelectionKey.OP_READ,
System.currentTimeMillis());
} catch (ClosedChannelException ex) {
//channel is already closed
}
}
}
);
((TCPSelectorHandler)
selectorHandler).setFinishIOUsingCurrentThread(origVal);
both work when there are no concurrent requests, but under load both
of them result in:
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
at java.util.HashMap$KeyIterator.next(HashMap.java:828)
at java.util.Collections$UnmodifiableCollection
$1.next(Collections.java:1010)
at
com
.sun
.grizzly
.http.SelectorThreadKeyHandler.expire(SelectorThreadKeyHandler.java:115)
at
com.sun.grizzly.TCPSelectorHandler.postSelect(TCPSelectorHandler.java:
556)
This is because both versions register the channel with the selector
in a thread that is not the SelectorThread, which is not thread safe.
Unfortunately I don't see a way to register a channel with the
selector in a thread safe manner without modifying TCPSelectorHandler
and adding a concurrent queue to it for operations that will get
executed by the thread handling Selector#select operations.
Thoughts?
/i
On Jan 14, 2010, at 9:57 AM, Igor Minar wrote:
> Hi Alexey,
>
> You are just proving that I'm not familiar with all the details of
> the grizzly apis I'm using. I did not know that returning false from
> the filter has this effect. I'll give it a shot tonight!
>
> thanks,
> Igor
>
>
> On Jan 14, 2010, at 9:48 AM, Oleksiy Stashok wrote:
>
>> Hi Igor,
>>
>> probably you've already tried that.... anyway
>> IMO, when you decide to intercept connection to execute sendfile
>> asynchronously, you can return false from AsyncFilter. This way
>> you'll be able to keep ProcessorTask, so at time when sendfile will
>> complete its task - you can register Channel back to the main
>> SelectorThread and resume connection using saved ProcessorTask.
>>
>> Please correct me, if something is missed.
>>
>> Thanks.
>>
>> WBR,
>> Alexey.
>>
>>> As I mentioned in my previous email, I expect that support for
>>> http keep-alive (persistent connections) is another major feature
>>> that could push the performance of grizzly-sendfile even further.
>>> I believe that with the current grizzly (1.9) api, it is not
>>> possible for me to get this done, but I might be wrong. In any
>>> case, I'd appreciate help with either pointing me to the right api
>>> or discussing changes to grizzly needed in order to make this
>>> possible.
>>>
>>>
>>> How grizzly-sendfile integrates with grizzly:
>>>
>>> grizzly-sendfile works as an async filter for grizzly, which
>>> intercepts all the http requests and when it determines that it is
>>> being requested to download a static file, it deregisters the
>>> current socket channel from the grizzly's selector, returns from
>>> the filter and asynchronously does its best to efficiently send
>>> the requested file to the client and then closes the connection.
>>>
>>>
>>> The problem:
>>>
>>> Ending the transmission like this results in non-persistent
>>> connections (Connection: close), which come with a significant
>>> performance penalty compared to keep-alive (persistent) connections.
>>>
>>> I'd much rather return the connection back to grizzly and put it
>>> in its keep alive queue or list, so that it could be reused for
>>> future transmissions. However because of the async implementation
>>> by the time when the download is finished, the ProcessorTask that
>>> was provided to my filter is long gone (recycled, or possibly in
>>> use by a different request), and the only thing I'm left with is
>>> the actual socket channel.
>>>
>>>
>>> The question:
>>>
>>> Is there a way for me to take a channel and make grizzly accept it
>>> as a channel that should be monitored for further requests? Or is
>>> there a better way to integrate with grizzly that would allow me
>>> to achieve my keep-alive goal?
>>>
>>> To get an idea of what I'm doing without digging through all of
>>> the grizzly-sendfile's code, I suggest that you have a look at my
>>> async filter [1], which is the integration point between grizzly
>>> and grizzly-sendfile. Just keep in mind that when a Download[2] is
>>> prepared in the filter and registered with my selector thread, the
>>> filter returns. The Download is then processed asynchronously by
>>> grizzly-sendfile threads.
>>>
>>> thanks,
>>> Igor
>>>
>>>
>>> [1] https://hg.kenai.com/hg/grizzly-sendfile~main/file/
>>> a68e8b200193/grizzly-sendfile-g19/src/main/java/com/igorminar/
>>> grizzlysendfile/SendfileFilter.java#l1
>>> [2] https://hg.kenai.com/hg/grizzly-sendfile~main/file/
>>> a68e8b200193/sendfile-core/src/main/java/com/igorminar/
>>> grizzlysendfile/Download.java#l1
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
>>> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>>>
>>
>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
>> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>>
>