Long-Polling is now more robust and requires less CPU resources.

This commit is contained in:
marcins78@gmail.com
2013-01-19 14:01:18 +00:00
parent b1eba473f1
commit 9ed9586644
12 changed files with 128 additions and 100 deletions

View File

@@ -90,15 +90,6 @@ public class ChatRequestHandler extends LotroServerRequestHandler implements Uri
_responseWriter = responseWriter;
}
@Override
public boolean isChanged() {
try {
return _chatRoom.getChatRoomListener(_playerId).hasMessages();
} catch (SubscriptionExpiredException e) {
return true;
}
}
@Override
public synchronized boolean wasProcessed() {
return _processed;

View File

@@ -112,19 +112,6 @@ public class GameRequestHandler extends LotroServerRequestHandler implements Uri
_responseWriter = responseWriter;
}
@Override
public boolean isChanged() {
try {
return _gameMediator.getCommunicationChannel(_resourceOwner, _channelNumber).hasGameEvents();
} catch (PrivateInformationException e) {
return true;
} catch (SubscriptionExpiredException e) {
return true;
} catch (SubscriptionConflictException e) {
return true;
}
}
@Override
public synchronized boolean wasProcessed() {
return _processed;

View File

@@ -465,17 +465,6 @@ public class HallRequestHandler extends LotroServerRequestHandler implements Uri
_responseWriter = responseWriter;
}
@Override
public boolean isChanged() {
try {
return _hallServer.getCommunicationChannel(_resourceOwner, _channelNumber).hasChangesInCommunicationChannel();
} catch (SubscriptionExpiredException e) {
return true;
} catch (SubscriptionConflictException e) {
return true;
}
}
@Override
public synchronized boolean wasProcessed() {
return _processed;

View File

@@ -11,6 +11,7 @@ var GempLotrHallUI = Class.extend({
buttonsDiv:null,
pocketDiv:null,
pocketValue:null,
hallChannelId: null,
init:function (div, url, chat) {
@@ -422,8 +423,11 @@ var GempLotrHallUI = Class.extend({
if (root.tagName == "hall") {
this.hallChannelId = root.getAttribute("channelNumber");
var currency = root.getAttribute("currency");
this.pocketDiv.html(formatPrice(currency));
var currency = parseInt(root.getAttribute("currency"));
if (currency != this.pocketValue) {
this.pocketValue = currency;
this.pocketDiv.html(formatPrice(currency));
}
var motd = root.getAttribute("motd");
if (motd != null)

View File

@@ -1,7 +1,13 @@
package com.gempukku.polling;
public interface LongPollableResource {
public void registerForChanges(LongPollingResource longPollingResource);
/**
* Registers the request for changes, however if there are any changes that can be consumed immediatelly, then
* true is returned.
* @param waitingRequest
* @return
*/
public boolean registerRequest(WaitingRequest waitingRequest);
public void deregisterResource(LongPollingResource longPollingResource);
public void deregisterRequest(WaitingRequest waitingRequest);
}

View File

@@ -1,8 +1,6 @@
package com.gempukku.polling;
public interface LongPollingResource {
public boolean isChanged();
public boolean wasProcessed();
public void processIfNotProcessed();

View File

@@ -2,33 +2,40 @@ package com.gempukku.polling;
import org.apache.log4j.Logger;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LongPollingSystem {
private static Logger _log = Logger.getLogger(LongPollingSystem.class);
private ConcurrentLinkedQueue<TimeoutResource> _waitingActions = new ConcurrentLinkedQueue<TimeoutResource>();
private final Set<ResourceWaitingRequest> _waitingActions = Collections.synchronizedSet(new HashSet<ResourceWaitingRequest>());
private long _pollingInterval = 1000;
private long _pollingLength = 10000;
private ProcessingRunnable _processingRunnable;
private ProcessingRunnable _timeoutRunnable;
private ExecutorService _executorService = Executors.newCachedThreadPool();
public LongPollingSystem() {
}
public void start() {
_processingRunnable = new ProcessingRunnable();
Thread thr = new Thread(_processingRunnable);
_timeoutRunnable = new ProcessingRunnable();
Thread thr = new Thread(_timeoutRunnable);
thr.start();
}
public void processLongPollingResource(LongPollingResource resource, LongPollableResource pollableResource) {
if (resource.isChanged())
resource.processIfNotProcessed();
else
pollableResource.registerForChanges(resource);
_waitingActions.add(new TimeoutResource(resource, pollableResource, System.currentTimeMillis()));
ResourceWaitingRequest request = new ResourceWaitingRequest(pollableResource, resource, System.currentTimeMillis());
if (pollableResource.registerRequest(request)) {
execute(resource);
} else {
_waitingActions.add(request);
}
}
private void pause() {
@@ -39,21 +46,38 @@ public class LongPollingSystem {
}
}
private void processWaitingRequest(final ResourceWaitingRequest request) {
_waitingActions.remove(request);
execute(request.getLongPollingResource());
}
private void execute(final LongPollingResource resource) {
_executorService.submit(
new Runnable() {
@Override
public void run() {
resource.processIfNotProcessed();
}
});
}
private class ProcessingRunnable implements Runnable {
@Override
public void run() {
while (true) {
long now = System.currentTimeMillis();
Iterator<TimeoutResource> iterator = _waitingActions.iterator();
while (iterator.hasNext()) {
TimeoutResource resource = iterator.next();
if (resource.getLongPollingResource().wasProcessed())
iterator.remove();
else {
if (resource.getStart() + _pollingLength < now) {
resource.getLongPollableResource().deregisterResource(resource.getLongPollingResource());
resource.getLongPollingResource().processIfNotProcessed();
synchronized (_waitingActions) {
long now = System.currentTimeMillis();
Iterator<ResourceWaitingRequest> iterator = _waitingActions.iterator();
while (iterator.hasNext()) {
ResourceWaitingRequest waitingRequest = iterator.next();
if (waitingRequest.getLongPollingResource().wasProcessed())
iterator.remove();
else {
if (waitingRequest.getStart() + _pollingLength < now) {
waitingRequest.getLongPollableResource().deregisterRequest(waitingRequest);
iterator.remove();
execute(waitingRequest.getLongPollingResource());
}
}
}
}
@@ -62,17 +86,22 @@ public class LongPollingSystem {
}
}
private class TimeoutResource {
private long _start;
private LongPollableResource _longPollableResource;
private class ResourceWaitingRequest implements WaitingRequest {
private LongPollingResource _longPollingResource;
private LongPollableResource _longPollableResource;
private long _start;
private TimeoutResource(LongPollingResource longPollingResource, LongPollableResource longPollableResource, long start) {
_longPollingResource = longPollingResource;
private ResourceWaitingRequest(LongPollableResource longPollableResource, LongPollingResource longPollingResource, long start) {
_longPollableResource = longPollableResource;
_longPollingResource = longPollingResource;
_start = start;
}
@Override
public void processRequest() {
processWaitingRequest(this);
}
public LongPollableResource getLongPollableResource() {
return _longPollableResource;
}

View File

@@ -0,0 +1,5 @@
package com.gempukku.polling;
public interface WaitingRequest {
public void processRequest();
}

View File

@@ -3,21 +3,20 @@ package com.gempukku.lotro.game.state;
import com.gempukku.lotro.common.Token;
import com.gempukku.lotro.communication.GameStateListener;
import com.gempukku.lotro.game.PhysicalCard;
import static com.gempukku.lotro.game.state.GameEvent.Type.*;
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
import com.gempukku.lotro.logic.timing.GameStats;
import com.gempukku.polling.LongPollableResource;
import com.gempukku.polling.LongPollingResource;
import com.gempukku.polling.WaitingRequest;
import java.util.*;
import static com.gempukku.lotro.game.state.GameEvent.Type.*;
public class GameCommunicationChannel implements GameStateListener, LongPollableResource {
private List<GameEvent> _events = new LinkedList<GameEvent>();
private String _self;
private long _lastConsumed = System.currentTimeMillis();
private int _channelNumber;
private LongPollingResource _longPollingResource;
private WaitingRequest _waitingRequest;
public GameCommunicationChannel(String self, int channelNumber) {
_self = self;
@@ -37,19 +36,23 @@ public class GameCommunicationChannel implements GameStateListener, LongPollable
}
@Override
public synchronized void deregisterResource(LongPollingResource longPollingResource) {
_longPollingResource = null;
public synchronized void deregisterRequest(WaitingRequest waitingRequest) {
_waitingRequest = null;
}
@Override
public synchronized void registerForChanges(LongPollingResource longPollingResource) {
_longPollingResource = longPollingResource;
public synchronized boolean registerRequest(WaitingRequest waitingRequest) {
if (_events.size()>0)
return true;
_waitingRequest = waitingRequest;
return false;
}
private synchronized void appendEvent(GameEvent event) {
_events.add(event);
if (_longPollingResource != null)
_longPollingResource.processIfNotProcessed();
if (_waitingRequest != null)
_waitingRequest.processRequest();
}
private int[] getCardIds(Collection<PhysicalCard> cards) {

View File

@@ -2,14 +2,15 @@ package com.gempukku.lotro.draft;
import com.gempukku.lotro.game.CardCollection;
import com.gempukku.polling.LongPollableResource;
import com.gempukku.polling.LongPollingResource;
import com.gempukku.polling.WaitingRequest;
public class DraftCommunicationChannel implements LongPollableResource {
private int _channelNumber;
private long _lastAccessed;
private boolean _changed;
private String _cardChoiceOnClient;
private LongPollingResource _longPollingResource;
private WaitingRequest _waitingRequest;
public DraftCommunicationChannel(int channelNumber) {
_channelNumber = channelNumber;
@@ -28,18 +29,23 @@ public class DraftCommunicationChannel implements LongPollableResource {
}
@Override
public void deregisterResource(LongPollingResource longPollingResource) {
_longPollingResource = null;
public synchronized void deregisterRequest(WaitingRequest waitingRequest) {
_waitingRequest = null;
}
@Override
public void registerForChanges(LongPollingResource longPollingResource) {
_longPollingResource = longPollingResource;
public boolean registerRequest(WaitingRequest waitingRequest) {
if (_changed)
return true;
_waitingRequest = waitingRequest;
return false;
}
public synchronized void draftChanged() {
if (_longPollingResource != null)
_longPollingResource.processIfNotProcessed();
_changed = true;
if (_waitingRequest != null)
_waitingRequest.processRequest();
}
public boolean hasChangesInCommunicationChannel(DraftCardChoice draftCardChoice) {
@@ -59,7 +65,7 @@ public class DraftCommunicationChannel implements LongPollableResource {
return sb.toString();
}
public void processCommunicationChannel(DraftCardChoice draftCardChoice, CardCollection chosenCards, DraftChannelVisitor draftChannelVisitor) {
public synchronized void processCommunicationChannel(DraftCardChoice draftCardChoice, CardCollection chosenCards, DraftChannelVisitor draftChannelVisitor) {
updateLastAccess();
CardCollection cardCollection = draftCardChoice.getCardCollection();
@@ -72,5 +78,7 @@ public class DraftCommunicationChannel implements LongPollableResource {
_cardChoiceOnClient = null;
}
draftChannelVisitor.chosenCards(chosenCards);
_changed = false;
}
}

View File

@@ -3,7 +3,7 @@ package com.gempukku.lotro.game;
import com.gempukku.lotro.chat.ChatMessage;
import com.gempukku.lotro.chat.ChatRoomListener;
import com.gempukku.polling.LongPollableResource;
import com.gempukku.polling.LongPollingResource;
import com.gempukku.polling.WaitingRequest;
import java.util.LinkedList;
import java.util.List;
@@ -11,23 +11,27 @@ import java.util.List;
public class ChatCommunicationChannel implements ChatRoomListener, LongPollableResource {
private List<ChatMessage> _messages = new LinkedList<ChatMessage>();
private long _lastConsumed = System.currentTimeMillis();
private LongPollingResource _longPollingResource;
private WaitingRequest _waitingRequest;
@Override
public synchronized void deregisterResource(LongPollingResource longPollingResource) {
_longPollingResource = null;
public synchronized void deregisterRequest(WaitingRequest waitingRequest) {
_waitingRequest = null;
}
@Override
public synchronized void registerForChanges(LongPollingResource longPollingResource) {
_longPollingResource = longPollingResource;
public synchronized boolean registerRequest(WaitingRequest waitingRequest) {
if (_messages.size()>0)
return true;
_waitingRequest = waitingRequest;
return false;
}
@Override
public synchronized void messageReceived(ChatMessage message) {
_messages.add(message);
if (_longPollingResource != null)
_longPollingResource.processIfNotProcessed();
if (_waitingRequest != null)
_waitingRequest.processRequest();
}
public synchronized List<ChatMessage> consumeMessages() {

View File

@@ -2,7 +2,7 @@ package com.gempukku.lotro.hall;
import com.gempukku.lotro.game.Player;
import com.gempukku.polling.LongPollableResource;
import com.gempukku.polling.LongPollingResource;
import com.gempukku.polling.WaitingRequest;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.mutable.MutableObject;
@@ -17,26 +17,30 @@ public class HallCommunicationChannel implements LongPollableResource {
private Map<String, Map<String, String>> _tablePropsOnClient = new LinkedHashMap<String, Map<String, String>>();
private Set<String> _playedGames = new HashSet<String>();
private boolean _changed;
private LongPollingResource _longPollingResource;
private WaitingRequest _waitingRequest;
public HallCommunicationChannel(int channelNumber) {
_channelNumber = channelNumber;
}
@Override
public synchronized void deregisterResource(LongPollingResource longPollingResource) {
_longPollingResource = null;
public synchronized void deregisterRequest(WaitingRequest waitingRequest) {
_waitingRequest = null;
}
@Override
public synchronized void registerForChanges(LongPollingResource longPollingResource) {
_longPollingResource = longPollingResource;
public boolean registerRequest(WaitingRequest waitingRequest) {
if (_changed)
return true;
_waitingRequest = waitingRequest;
return false;
}
public synchronized void hallChanged() {
_changed = true;
if (_longPollingResource != null)
_longPollingResource.processIfNotProcessed();
if (_waitingRequest != null)
_waitingRequest.processRequest();
}
public int getChannelNumber() {