Хэндшейк, сервисы, аннотационные блокировки в протоколе, репозитории
This commit is contained in:
@@ -11,6 +11,7 @@ import org.java_websocket.handshake.ClientHandshake;
|
||||
import org.java_websocket.server.WebSocketServer;
|
||||
|
||||
import io.orprotocol.client.Client;
|
||||
import io.orprotocol.lock.ThreadLocker;
|
||||
import io.orprotocol.packet.Packet;
|
||||
import io.orprotocol.packet.PacketExecutor;
|
||||
import io.orprotocol.packet.PacketManager;
|
||||
@@ -22,6 +23,7 @@ public class Server extends WebSocketServer {
|
||||
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
private Context context;
|
||||
private ServerListener listener;
|
||||
private ThreadLocker threadLocker = new ThreadLocker();
|
||||
|
||||
/**
|
||||
* Конструктор сервера
|
||||
@@ -144,10 +146,27 @@ public class Server extends WebSocketServer {
|
||||
*/
|
||||
return;
|
||||
}
|
||||
executor.onPacketReceived(packet, client);
|
||||
/**
|
||||
* Проверяем наличие блокировки для данного пакета и ключа в аннотации @Lock.
|
||||
*/
|
||||
if(!threadLocker.acquireLock(packet, executorClass)) {
|
||||
/**
|
||||
* Если блокировка уже существует, значит другой поток обрабатывает пакет
|
||||
* с таким же значением lockFor, отклоняем текущий пакет.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
try {
|
||||
executor.onPacketReceived(packet, client);
|
||||
} finally {
|
||||
/**
|
||||
* Снимаем блокировку после обработки пакета.
|
||||
*/
|
||||
threadLocker.releaseLock(packet, executorClass);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Error while processing packet " + packetClass.getName());
|
||||
System.out.println(e.getStackTrace());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ public class Client {
|
||||
this.eciTags = new HashMap<Class<? extends ECITag>, ECITag>();
|
||||
this.heartbeatInterval = heartbeatInterval;
|
||||
this.lastHeartbeatTime = System.currentTimeMillis();
|
||||
this.packetManager = new PacketManager();
|
||||
this.packetManager = packetManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,7 +57,7 @@ public class Client {
|
||||
* @return
|
||||
*/
|
||||
public boolean isAlive() {
|
||||
return (System.currentTimeMillis() - this.lastHeartbeatTime) * 2 <= this.heartbeatInterval * 1000;
|
||||
return (System.currentTimeMillis() - this.lastHeartbeatTime) <= ((this.heartbeatInterval * 1000) * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
23
src/main/java/io/orprotocol/lock/Lock.java
Normal file
23
src/main/java/io/orprotocol/lock/Lock.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package io.orprotocol.lock;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Аннотация для указания блокировки
|
||||
* при обработке пакета.
|
||||
* Мультипоточная блокировка, то есть блокировка сработает только
|
||||
* если другой поток уже обрабатывает пакет с полем lockFor, однако другие потоки
|
||||
* могут обрабатывать пакеты с другими значениями lockFor параллельно.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface Lock {
|
||||
/**
|
||||
* По какому полю в пакете
|
||||
* будет осуществляться блокировка
|
||||
*/
|
||||
String lockFor();
|
||||
}
|
||||
82
src/main/java/io/orprotocol/lock/ThreadLocker.java
Normal file
82
src/main/java/io/orprotocol/lock/ThreadLocker.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package io.orprotocol.lock;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.orprotocol.client.Client;
|
||||
import io.orprotocol.packet.Packet;
|
||||
import io.orprotocol.packet.PacketExecutor;
|
||||
|
||||
/**
|
||||
* Менеджер блокировок для обработки пакетов с аннотацией @Lock.
|
||||
*/
|
||||
public class ThreadLocker {
|
||||
|
||||
private final ConcurrentHashMap<String, Boolean> locks = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Пытается захватить блокировку для указанного пакета и ключа в аннотации @Lock.
|
||||
* @param packet Пакет для которого требуется блокировка
|
||||
* @param exectuor Класс исполнителя пакета
|
||||
* @return true, если блокировка успешно захвачена, иначе false.
|
||||
*/
|
||||
public boolean acquireLock(Packet packet, Class<? extends PacketExecutor> exectuor) {
|
||||
try{
|
||||
Method prMethod = exectuor.getMethod("onPacketReceived", Packet.class, Client.class);
|
||||
if(prMethod == null) {
|
||||
return true;
|
||||
}
|
||||
Lock lockAnnotation = prMethod.getAnnotation(Lock.class);
|
||||
if(lockAnnotation == null) {
|
||||
return true;
|
||||
}
|
||||
String fieldName = lockAnnotation.lockFor();
|
||||
Field field = packet.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
String fieldValue = (String) field.get(packet);
|
||||
String lockValue = packet.getClass().getName() + "_" + fieldValue;
|
||||
if(locks.putIfAbsent(lockValue, true) != null) {
|
||||
/**
|
||||
* Если блокировка уже существует, значит другой поток обрабатывает пакет
|
||||
* с таким же значением lockFor, отклоняем текущий пакет.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}catch(Exception e) {
|
||||
/**
|
||||
* Игнорируем ошибки при попытке блокировки,
|
||||
* чтобы не блокировать обработку пакета из-за ошибок рефлексии
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Освобождает блокировку для указанного пакета и ключа в аннотации @Lock.
|
||||
* @param packet Пакет для которого требуется разблокировка
|
||||
* @param exectuor Класс исполнителя пакета
|
||||
*/
|
||||
public void releaseLock(Packet packet, Class<? extends PacketExecutor> exectuor) {
|
||||
try{
|
||||
Method prMethod = exectuor.getMethod("onPacketReceived", Packet.class, Client.class);
|
||||
if(prMethod == null) {
|
||||
return;
|
||||
}
|
||||
Lock lockAnnotation = prMethod.getAnnotation(Lock.class);
|
||||
if(lockAnnotation == null) {
|
||||
return;
|
||||
}
|
||||
String fieldName = lockAnnotation.lockFor();
|
||||
Field field = packet.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
String fieldValue = (String) field.get(packet);
|
||||
String lockValue = packet.getClass().getName() + "_" + fieldValue;
|
||||
locks.remove(lockValue);
|
||||
}catch(Exception e) {
|
||||
// Игнорируем ошибки при разблокировке
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user