Я работаю над подсчетом количества пользователей на странице. В основном, когда пользователь просматривает URL-адрес localhost:8080/itemDetail.do?itemId=1
, страница будет показывать, сколько пользователей просматривает эту страницу одновременно.Подсчет текущих пользователей, просматривающих страницу
Solution подход
Когда пользователь просматривает определенную страницу, эта страница будет продолжать посылать запрос AJAX каждый 1 второй обратно на сервер, то сервер будет использовать Map<String, Map<String, Integer>>
(оба используют ConcurrentHashMap
для инициализации) в содержат itemId
(чтобы узнать, какая страница просматривается) и IP
и timeout
счет (в помещении Map
). Каждый раз, когда вы получаете запрос, подсчет увеличивается 1. Во время процесса запроса поток пропускается, чтобы уменьшить таймаут, подсчитывая каждые 2 секунды. Если в конце подсчет таймаута равен 0, приложение рассмотрит, что пользователь остановил просмотр страницы, тогда поток удалит эту запись, и, следовательно, количество пользователей будет уменьшено. 1. Небольшая проблема для этого подхода - это время ожидания число увеличивается быстрее, чем уменьшается, если пользователь достаточно долго открывает страницу и закрывает страницу, тогда приложение будет знать, что этот пользователь уже покинул страницу, потому что номер тайм-аута в этот момент довольно большой
Реализация
// Controller
@RequestMapping(value = AppConstants.ACTION_NUMBER_VIEWING, method = GET)
@ResponseBody
public int userItemViewing(HttpServletRequest request, @RequestParam(value = "itemId") String itemId) {
try {
return eventService.countUserOnline(request, itemId);
} catch (CAServiceException e) {
e.printStackTrace();
LOG.error("========== Error when counting number of online user ==========" + e.getMessage());
}
return 0;
}
// Service
private static Map<String, Map<String, Integer>> numberOfCurrentViews;
private Thread countDownOnlineThread;
class OnlineCountingDownRunnable implements Runnable {
private List<String> timeoutList = new ArrayList<String>();
private void cleanTimeoutIps() {
for (String itemId : numberOfCurrentViews.keySet()) {
Map<String, Integer> currentIps = numberOfCurrentViews.get(itemId);
for(String ip : timeoutList){
currentIps.remove(ip);
}
}
}
@Override
public void run() {
try {
for (String itemId : numberOfCurrentViews.keySet()) {
Map<String, Integer> currentIps = numberOfCurrentViews.get(itemId);
for(String ip : currentIps.keySet()){
Integer timeout = new Integer(currentIps.get(ip).intValue() - 1);
if (timeout == 0) {
timeoutList.add(ip);
}
currentIps.put(ip, timeout);
}
}
cleanTimeoutIps();
// counting down time must be double increasing time
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
LOG.error("---------------- Thread error in counting down online user: " + e.getMessage());
}
}
}
public int countUserOnline(HttpServletRequest request, String itemId) throws CAServiceException {
// create a count down timer to detect if the user does not view the page anymore
String ip = request.getRemoteAddr();
// init counting down online user map
if (numberOfCurrentViews == null) {
numberOfCurrentViews = new ConcurrentHashMap<String, Map<String, Integer>>();
}
// start thread to check user online
if (countDownOnlineThread == null) {
countDownOnlineThread = new Thread(new OnlineCountingDownRunnable());
countDownOnlineThread.start();
}
LOG.debug("---------------- Requested IP: " + ip);
if (ip == null || ip.isEmpty()) {
throw new CAServiceException("======= Cannot detect Ip of the client =======");
}
if (numberOfCurrentViews.get(itemId) != null) {
Map<String, Integer> userView = numberOfCurrentViews.get(itemId);
if (userView.get(ip) != null) {
userView.put(ip, userView.get(ip).intValue() + 1);
} else {
userView.put(ip, 1);
}
numberOfCurrentViews.put(itemId, userView);
} else {
Map<String, Integer> ips = new ConcurrentHashMap<String, Integer>();
ips.put(ip, 1);
numberOfCurrentViews.put(itemId, ips);
}
LOG.debug(String.format(
"============= %s is seeing there is %s users viewing item %s =============",
ip, numberOfCurrentViews.get(itemId).size(), itemId
));
return numberOfCurrentViews.get(itemId).size();
}
Проблемы
Я понятия не имею, как протестировать эту функцию, поскольку для просмотра страницы требуется несколько IP-адресов. Я пытался настроить JMeter и настроить IP-спуфинг, как этот link но не увенчалась успехом, так что я сделал небольшой тест макет, как это, чтобы просмотреть журнал
@Test
public void testCountUserOnline() throws Exception {
List<HttpServletRequest> requests = new ArrayList<HttpServletRequest>();
for (int i = 0; i < 10; i ++) {
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(request.getRemoteAddr()).thenReturn(String.format("192.168.1.%s", i));
requests.add(request);
}
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i ++) {
Thread thread = new Thread(new RequestRunnable(requests.get(i)));
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
}
class RequestRunnable implements Runnable {
private HttpServletRequest request;
public RequestRunnable(HttpServletRequest request) {
this.request = request;
}
public void run() {
try {
int i = 0;
while (i < 10) {
eventService.countUserOnline(request, "1");
i++;
System.out.println(i);
Thread.sleep(1000);
}
} catch (CAServiceException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
но опять же, я не так уверен, о реализация
Также, это обычный способ использования человеком, чтобы подсчитать количество пользователей на странице? Я просто хочу удостовериться, что я ничего не пропущу в случае, если есть какой-либо ярлык для этой части. Я использую Spring MVC 3.x
. Мне нужно поддерживать IE 9, а также: (((, так Web Socket
не может широко использоваться
Это должно быть комментарием, а не ответ ... –