NodeLatencyVerifier
- Using an HttpUrl instance as a key to the map of FullNode instances, we were using simple String here before, but turned out not to be too reliable NetworkService - Introducing the NodeLatencyVerifier instance into the NetworkService, and making its use optional - In case the user opts to use the node-latency verification, the initial connection is delayed until we obtain a measurement of all nodes - Exposing a PublishSubject, this allows users of the library to get notified of the node latency measurements NetworkServiceManager - A boolean flag is now used to decide whether or not to start a NetworkService instance with the node-latency verification feature ON
This commit is contained in:
parent
776630dd57
commit
ede7265989
5 changed files with 179 additions and 24 deletions
|
@ -50,11 +50,16 @@ import cy.agorise.graphenej.models.JsonRpcResponse;
|
||||||
import cy.agorise.graphenej.models.OperationHistory;
|
import cy.agorise.graphenej.models.OperationHistory;
|
||||||
import cy.agorise.graphenej.network.FullNode;
|
import cy.agorise.graphenej.network.FullNode;
|
||||||
import cy.agorise.graphenej.network.LatencyNodeProvider;
|
import cy.agorise.graphenej.network.LatencyNodeProvider;
|
||||||
|
import cy.agorise.graphenej.network.NodeLatencyVerifier;
|
||||||
import cy.agorise.graphenej.network.NodeProvider;
|
import cy.agorise.graphenej.network.NodeProvider;
|
||||||
import cy.agorise.graphenej.operations.CustomOperation;
|
import cy.agorise.graphenej.operations.CustomOperation;
|
||||||
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
import cy.agorise.graphenej.operations.LimitOrderCreateOperation;
|
||||||
import cy.agorise.graphenej.operations.TransferOperation;
|
import cy.agorise.graphenej.operations.TransferOperation;
|
||||||
|
import io.reactivex.Observer;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.annotations.Nullable;
|
import io.reactivex.annotations.Nullable;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.subjects.PublishSubject;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
@ -76,6 +81,8 @@ public class NetworkService extends Service {
|
||||||
|
|
||||||
public static final String KEY_REQUESTED_APIS = "key_requested_apis";
|
public static final String KEY_REQUESTED_APIS = "key_requested_apis";
|
||||||
|
|
||||||
|
public static final String KEY_ENABLE_LATENCY_VERIFIER = "key_enable_latency_verifier";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared preference
|
* Shared preference
|
||||||
*/
|
*/
|
||||||
|
@ -112,7 +119,17 @@ public class NetworkService extends Service {
|
||||||
// Variable used to keep track of the currently obtained API accesses
|
// Variable used to keep track of the currently obtained API accesses
|
||||||
private HashMap<Integer, Integer> mApiIds = new HashMap<Integer, Integer>();
|
private HashMap<Integer, Integer> mApiIds = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
NodeProvider nodeProvider = new LatencyNodeProvider();
|
// Variable used as a source of node information
|
||||||
|
private NodeProvider nodeProvider = new LatencyNodeProvider();
|
||||||
|
|
||||||
|
// Class used to obtain frequent node latency updates
|
||||||
|
private NodeLatencyVerifier nodeLatencyVerifier;
|
||||||
|
|
||||||
|
// PublishSubject used to announce full node latencies updates
|
||||||
|
private PublishSubject<FullNode> fullNodePublishSubject;
|
||||||
|
|
||||||
|
// Counter used to trigger the connection only after we've received enough node latency updates
|
||||||
|
private long latencyUpdateCounter;
|
||||||
|
|
||||||
private Gson gson = new GsonBuilder()
|
private Gson gson = new GsonBuilder()
|
||||||
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
.registerTypeAdapter(Transaction.class, new Transaction.TransactionDeserializer())
|
||||||
|
@ -144,7 +161,7 @@ public class NetworkService extends Service {
|
||||||
public void connect(){
|
public void connect(){
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
FullNode fullNode = nodeProvider.getBestNode();
|
FullNode fullNode = nodeProvider.getBestNode();
|
||||||
Log.d(TAG,"connect.url: "+fullNode.getUrl());
|
Log.v(TAG,"connect.url: "+fullNode.getUrl());
|
||||||
Request request = new Request.Builder().url(fullNode.getUrl()).build();
|
Request request = new Request.Builder().url(fullNode.getUrl()).build();
|
||||||
mWebSocket = client.newWebSocket(request, mWebSocketListener);
|
mWebSocket = client.newWebSocket(request, mWebSocketListener);
|
||||||
}
|
}
|
||||||
|
@ -197,18 +214,19 @@ public class NetworkService extends Service {
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
if(mWebSocket != null)
|
if(mWebSocket != null)
|
||||||
mWebSocket.close(NORMAL_CLOSURE_STATUS, null);
|
mWebSocket.close(NORMAL_CLOSURE_STATUS, null);
|
||||||
|
|
||||||
|
nodeLatencyVerifier.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
Log.d(TAG,"onBind.intent: "+intent);
|
|
||||||
// Retrieving credentials and requested API data from the shared preferences
|
// Retrieving credentials and requested API data from the shared preferences
|
||||||
mUsername = intent.getStringExtra(NetworkService.KEY_USERNAME);
|
mUsername = intent.getStringExtra(NetworkService.KEY_USERNAME);
|
||||||
mPassword = intent.getStringExtra(NetworkService.KEY_PASSWORD);
|
mPassword = intent.getStringExtra(NetworkService.KEY_PASSWORD);
|
||||||
mRequestedApis = intent.getIntExtra(NetworkService.KEY_REQUESTED_APIS, 0);
|
mRequestedApis = intent.getIntExtra(NetworkService.KEY_REQUESTED_APIS, 0);
|
||||||
mAutoConnect = intent.getBooleanExtra(NetworkService.KEY_AUTO_CONNECT, true);
|
mAutoConnect = intent.getBooleanExtra(NetworkService.KEY_AUTO_CONNECT, true);
|
||||||
|
boolean verifyNodeLatency = intent.getBooleanExtra(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, false);
|
||||||
|
|
||||||
ArrayList<String> nodeUrls = new ArrayList<>();
|
ArrayList<String> nodeUrls = new ArrayList<>();
|
||||||
// If the user of the library desires, a custom list of node URLs can
|
// If the user of the library desires, a custom list of node URLs can
|
||||||
|
@ -225,15 +243,61 @@ public class NetworkService extends Service {
|
||||||
// Adding the library-provided list of nodes second
|
// Adding the library-provided list of nodes second
|
||||||
nodeUrls.addAll(Arrays.asList(Nodes.NODE_URLS));
|
nodeUrls.addAll(Arrays.asList(Nodes.NODE_URLS));
|
||||||
|
|
||||||
|
// Feeding all node information to the NodeProvider instance
|
||||||
for(String nodeUrl : nodeUrls){
|
for(String nodeUrl : nodeUrls){
|
||||||
nodeProvider.addNode(new FullNode(nodeUrl));
|
nodeProvider.addNode(new FullNode(nodeUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mAutoConnect) connect();
|
// We only connect automatically if the auto-connect flag is true AND
|
||||||
|
// we are not going to care about node latency ordering.
|
||||||
|
if(mAutoConnect && !verifyNodeLatency) {
|
||||||
|
connect();
|
||||||
|
}else{
|
||||||
|
// In case we care about node latency ordering, we must first obtain
|
||||||
|
// a first round of measurements in order to be sure to select the
|
||||||
|
// best node.
|
||||||
|
if(verifyNodeLatency){
|
||||||
|
ArrayList<FullNode> fullNodes = new ArrayList<>();
|
||||||
|
for(String url : nodeUrls){
|
||||||
|
fullNodes.add(new FullNode(url));
|
||||||
|
}
|
||||||
|
nodeLatencyVerifier = new NodeLatencyVerifier(fullNodes);
|
||||||
|
fullNodePublishSubject = nodeLatencyVerifier.start();
|
||||||
|
fullNodePublishSubject.observeOn(AndroidSchedulers.mainThread()).subscribe(nodeLatencyObserver);
|
||||||
|
}
|
||||||
|
}
|
||||||
return mBinder;
|
return mBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observer used to be notified about node latency measurement updates.
|
||||||
|
*/
|
||||||
|
private Observer<FullNode> nodeLatencyObserver = new Observer<FullNode>() {
|
||||||
|
@Override
|
||||||
|
public void onSubscribe(Disposable d) { }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNext(FullNode fullNode) {
|
||||||
|
latencyUpdateCounter++;
|
||||||
|
// Updating the node with the new latency measurement
|
||||||
|
nodeProvider.updateNode(fullNode);
|
||||||
|
|
||||||
|
// Once we have the latency value of all available nodes,
|
||||||
|
// we can safely proceed to start the connection
|
||||||
|
if(latencyUpdateCounter == nodeProvider.getSortedNodes().size()){
|
||||||
|
connect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Throwable e) {
|
||||||
|
Log.e(TAG,"nodeLatencyObserver.onError.Msg: "+e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete() { }
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used for the client Binder. Because we know this service always
|
* Class used for the client Binder. Because we know this service always
|
||||||
* runs in the same process as its clients, we don't need to deal with IPC.
|
* runs in the same process as its clients, we don't need to deal with IPC.
|
||||||
|
@ -489,7 +553,7 @@ public class NetworkService extends Service {
|
||||||
Log.e(TAG,"onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage());
|
Log.e(TAG,"onFailure. Exception: "+t.getClass().getName()+", Msg: "+t.getMessage());
|
||||||
// Logging error stack trace
|
// Logging error stack trace
|
||||||
for(StackTraceElement element : t.getStackTrace()){
|
for(StackTraceElement element : t.getStackTrace()){
|
||||||
Log.e(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
|
Log.v(TAG,String.format("%s#%s:%s", element.getClassName(), element.getMethodName(), element.getLineNumber()));
|
||||||
}
|
}
|
||||||
// Registering current status
|
// Registering current status
|
||||||
isLoggedIn = false;
|
isLoggedIn = false;
|
||||||
|
@ -538,4 +602,12 @@ public class NetworkService extends Service {
|
||||||
public List<FullNode> getNodes(){
|
public List<FullNode> getNodes(){
|
||||||
return nodeProvider.getSortedNodes();
|
return nodeProvider.getSortedNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an observable that will notify its observers about node latency updates.
|
||||||
|
* @return Observer of {@link FullNode} instances.
|
||||||
|
*/
|
||||||
|
public PublishSubject<FullNode> getNodeLatencyObservable(){
|
||||||
|
return fullNodePublishSubject;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
||||||
private int mRequestedApis;
|
private int mRequestedApis;
|
||||||
private List<String> mCustomNodeUrls = new ArrayList<>();
|
private List<String> mCustomNodeUrls = new ArrayList<>();
|
||||||
private boolean mAutoConnect;
|
private boolean mAutoConnect;
|
||||||
|
private boolean mVerifyLatency;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runnable used to schedule a service disconnection once the app is not visible to the user for
|
* Runnable used to schedule a service disconnection once the app is not visible to the user for
|
||||||
|
@ -104,7 +105,8 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
||||||
.putExtra(NetworkService.KEY_PASSWORD, mPassword)
|
.putExtra(NetworkService.KEY_PASSWORD, mPassword)
|
||||||
.putExtra(NetworkService.KEY_REQUESTED_APIS, mRequestedApis)
|
.putExtra(NetworkService.KEY_REQUESTED_APIS, mRequestedApis)
|
||||||
.putExtra(NetworkService.KEY_CUSTOM_NODE_URLS, customNodes)
|
.putExtra(NetworkService.KEY_CUSTOM_NODE_URLS, customNodes)
|
||||||
.putExtra(NetworkService.KEY_AUTO_CONNECT, mAutoConnect);
|
.putExtra(NetworkService.KEY_AUTO_CONNECT, mAutoConnect)
|
||||||
|
.putExtra(NetworkService.KEY_ENABLE_LATENCY_VERIFIER, mVerifyLatency);
|
||||||
|
|
||||||
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
|
||||||
}
|
}
|
||||||
|
@ -182,6 +184,14 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
||||||
this.mAutoConnect = mAutoConnect;
|
this.mAutoConnect = mAutoConnect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isVerifyLatency() {
|
||||||
|
return mVerifyLatency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVerifyLatency(boolean mVerifyLatency) {
|
||||||
|
this.mVerifyLatency = mVerifyLatency;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used to create a {@link NetworkServiceManager} with specific attributes.
|
* Class used to create a {@link NetworkServiceManager} with specific attributes.
|
||||||
*/
|
*/
|
||||||
|
@ -191,27 +201,53 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
||||||
private int requestedApis;
|
private int requestedApis;
|
||||||
private List<String> customNodeUrls;
|
private List<String> customNodeUrls;
|
||||||
private boolean autoconnect = true;
|
private boolean autoconnect = true;
|
||||||
|
private boolean verifyNodeLatency;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the user name, if required to connect to a node.
|
||||||
|
* @param name User name
|
||||||
|
* @return The Builder instance
|
||||||
|
*/
|
||||||
public Builder setUserName(String name){
|
public Builder setUserName(String name){
|
||||||
this.username = name;
|
this.username = name;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the password, if required to connect to a node.
|
||||||
|
* @param password Password
|
||||||
|
* @return The Builder instance
|
||||||
|
*/
|
||||||
public Builder setPassword(String password){
|
public Builder setPassword(String password){
|
||||||
this.password = password;
|
this.password = password;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an integer with the requested APIs encoded as binary flags.
|
||||||
|
* @param apis Integer representing the different APIs we require from the node.
|
||||||
|
* @return The Builder instance
|
||||||
|
*/
|
||||||
public Builder setRequestedApis(int apis){
|
public Builder setRequestedApis(int apis){
|
||||||
this.requestedApis = apis;
|
this.requestedApis = apis;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a list of custom node URLs.
|
||||||
|
* @param nodeUrls List of custom full node URLs.
|
||||||
|
* @return The Builder instance
|
||||||
|
*/
|
||||||
public Builder setCustomNodeUrls(List<String> nodeUrls){
|
public Builder setCustomNodeUrls(List<String> nodeUrls){
|
||||||
this.customNodeUrls = nodeUrls;
|
this.customNodeUrls = nodeUrls;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a list of custom node URLs.
|
||||||
|
* @param nodeUrls List of custom full node URLs.
|
||||||
|
* @return The Builder instance
|
||||||
|
*/
|
||||||
public Builder setCustomNodeUrls(String nodeUrls){
|
public Builder setCustomNodeUrls(String nodeUrls){
|
||||||
String[] urls = nodeUrls.split(",");
|
String[] urls = nodeUrls.split(",");
|
||||||
for(String url : urls){
|
for(String url : urls){
|
||||||
|
@ -221,11 +257,33 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the autoconnect flag. This is true by default.
|
||||||
|
* @param autoConnect True if we want the service to connect automatically, false otherwise.
|
||||||
|
* @return The Builder instance
|
||||||
|
*/
|
||||||
public Builder setAutoConnect(boolean autoConnect){
|
public Builder setAutoConnect(boolean autoConnect){
|
||||||
this.autoconnect = autoConnect;
|
this.autoconnect = autoConnect;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the node-verification flag. This is false by default.
|
||||||
|
* @param verifyLatency True if we want the service to perform a latency analysis before connecting.
|
||||||
|
* @return The Builder instance.
|
||||||
|
*/
|
||||||
|
public Builder setNodeLatencyVerification(boolean verifyLatency){
|
||||||
|
this.verifyNodeLatency = verifyLatency;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method used to build a {@link NetworkServiceManager} instance with all of the characteristics
|
||||||
|
* passed as parameters.
|
||||||
|
* @param context A Context of the application package implementing
|
||||||
|
* this class.
|
||||||
|
* @return Instance of the NetworkServiceManager class.
|
||||||
|
*/
|
||||||
public NetworkServiceManager build(Context context){
|
public NetworkServiceManager build(Context context){
|
||||||
NetworkServiceManager manager = new NetworkServiceManager(context);
|
NetworkServiceManager manager = new NetworkServiceManager(context);
|
||||||
if(username != null) manager.setUserName(username);
|
if(username != null) manager.setUserName(username);
|
||||||
|
@ -233,6 +291,7 @@ public class NetworkServiceManager implements Application.ActivityLifecycleCallb
|
||||||
if(customNodeUrls != null) manager.setCustomNodeUrls(customNodeUrls);
|
if(customNodeUrls != null) manager.setCustomNodeUrls(customNodeUrls);
|
||||||
manager.setRequestedApis(requestedApis);
|
manager.setRequestedApis(requestedApis);
|
||||||
manager.setAutoConnect(autoconnect);
|
manager.setAutoConnect(autoconnect);
|
||||||
|
manager.setVerifyLatency(verifyNodeLatency);
|
||||||
return manager;
|
return manager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package cy.agorise.graphenej.network;
|
package cy.agorise.graphenej.network;
|
||||||
|
|
||||||
import cy.agorise.graphenej.stats.ExponentialMovingAverage;
|
import cy.agorise.graphenej.stats.ExponentialMovingAverage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that represents a full node and is used to keep track of its round-trip time measured in milliseconds.
|
* Class that represents a full node and is used to keep track of its round-trip time measured in milliseconds.
|
||||||
*/
|
*/
|
||||||
public class FullNode implements Comparable {
|
public class FullNode implements Comparable {
|
||||||
|
|
||||||
private String mUrl;
|
private String mUrl;
|
||||||
private ExponentialMovingAverage latency;
|
private ExponentialMovingAverage latency;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package cy.agorise.graphenej.network;
|
package cy.agorise.graphenej.network;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -9,7 +7,7 @@ import java.util.List;
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
|
|
||||||
public class LatencyNodeProvider implements NodeProvider {
|
public class LatencyNodeProvider implements NodeProvider {
|
||||||
private final String TAG = this.getClass().getName();
|
private final String TAG = "LatencyNodeProvider";
|
||||||
private PriorityQueue<FullNode> mFullNodeHeap;
|
private PriorityQueue<FullNode> mFullNodeHeap;
|
||||||
|
|
||||||
public LatencyNodeProvider(){
|
public LatencyNodeProvider(){
|
||||||
|
@ -55,8 +53,6 @@ public class LatencyNodeProvider implements NodeProvider {
|
||||||
for(FullNode fullNode : nodeList){
|
for(FullNode fullNode : nodeList){
|
||||||
if(fullNode != null){
|
if(fullNode != null){
|
||||||
nodeList.add(fullNode);
|
nodeList.add(fullNode);
|
||||||
}else{
|
|
||||||
Log.d(TAG,"Found a null node in getSortedNodes");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(nodeList);
|
Collections.sort(nodeList);
|
||||||
|
|
|
@ -2,12 +2,14 @@ package cy.agorise.graphenej.network;
|
||||||
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import cy.agorise.graphenej.api.android.NetworkService;
|
import cy.agorise.graphenej.api.android.NetworkService;
|
||||||
import io.reactivex.subjects.PublishSubject;
|
import io.reactivex.subjects.PublishSubject;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
@ -18,6 +20,7 @@ import okhttp3.WebSocketListener;
|
||||||
* Class that encapsulates the node latency verification task
|
* Class that encapsulates the node latency verification task
|
||||||
*/
|
*/
|
||||||
public class NodeLatencyVerifier {
|
public class NodeLatencyVerifier {
|
||||||
|
private final String TAG = this.getClass().getName();
|
||||||
|
|
||||||
public static final int DEFAULT_LATENCY_VERIFICATION_PERIOD = 5 * 1000;
|
public static final int DEFAULT_LATENCY_VERIFICATION_PERIOD = 5 * 1000;
|
||||||
|
|
||||||
|
@ -30,7 +33,7 @@ public class NodeLatencyVerifier {
|
||||||
// Subject used to publish the result to interested parties
|
// Subject used to publish the result to interested parties
|
||||||
private PublishSubject<FullNode> subject = PublishSubject.create();
|
private PublishSubject<FullNode> subject = PublishSubject.create();
|
||||||
|
|
||||||
private HashMap<String, FullNode> nodeURLMap = new HashMap<>();
|
private HashMap<HttpUrl, FullNode> nodeURLMap = new HashMap<>();
|
||||||
|
|
||||||
// private WebSocket webSocket;
|
// private WebSocket webSocket;
|
||||||
|
|
||||||
|
@ -100,10 +103,15 @@ public class NodeLatencyVerifier {
|
||||||
requestMap.put(fullNode.getUrl(), request);
|
requestMap.put(fullNode.getUrl(), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
client.newWebSocket(request, mWebSocketListener);
|
String normalURL = fullNode.getUrl().replace("wss://", "https://");
|
||||||
if(!nodeURLMap.containsKey(fullNode.getUrl())){
|
Log.d(TAG,"normal URL : "+normalURL);
|
||||||
nodeURLMap.put(fullNode.getUrl(), fullNode);
|
if(!nodeURLMap.containsKey(fullNode.getUrl().replace("wss://", "https://"))){
|
||||||
|
HttpUrl key = HttpUrl.parse(normalURL);
|
||||||
|
Log.i(TAG, "Inserting key: "+key.toString());
|
||||||
|
nodeURLMap.put(key, fullNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.newWebSocket(request, mWebSocketListener);
|
||||||
}
|
}
|
||||||
mHandler.postDelayed(this, verificationPeriod);
|
mHandler.postDelayed(this, verificationPeriod);
|
||||||
}
|
}
|
||||||
|
@ -125,14 +133,32 @@ public class NodeLatencyVerifier {
|
||||||
handleResponse(webSocket, response);
|
handleResponse(webSocket, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method used to handle the node's first response. The idea here is to obtain
|
||||||
|
* the RTT (Round Trip Time) measurement and publish it using the PublishSubject.
|
||||||
|
*
|
||||||
|
* @param webSocket Websocket instance
|
||||||
|
* @param response Response instance
|
||||||
|
*/
|
||||||
private void handleResponse(WebSocket webSocket, Response response){
|
private void handleResponse(WebSocket webSocket, Response response){
|
||||||
String url = "wss://" + webSocket.request().url().host() + webSocket.request().url().encodedPath();
|
// Obtaining the HttpUrl instance that was previously used as a key
|
||||||
FullNode fullNode = nodeURLMap.get(url);
|
HttpUrl url = webSocket.request().url();
|
||||||
long after = System.currentTimeMillis();
|
if(nodeURLMap.containsKey(url)){
|
||||||
long before = timestamps.get(fullNode);
|
FullNode fullNode = nodeURLMap.get(url);
|
||||||
long delay = after - before;
|
long after = System.currentTimeMillis();
|
||||||
fullNode.addLatencyValue(delay);
|
long before = timestamps.get(fullNode);
|
||||||
subject.onNext(fullNode);
|
long delay = after - before;
|
||||||
|
fullNode.addLatencyValue(delay);
|
||||||
|
subject.onNext(fullNode);
|
||||||
|
}else{
|
||||||
|
// We cannot properly handle a response to a request whose
|
||||||
|
// URL was not registered at the nodeURLMap. This is because without this,
|
||||||
|
// we cannot know to which node this response corresponds. This should not happen.
|
||||||
|
Log.e(TAG,"nodeURLMap does not contain url: "+url);
|
||||||
|
for(HttpUrl key : nodeURLMap.keySet()){
|
||||||
|
Log.e(TAG,"> "+key);
|
||||||
|
}
|
||||||
|
}
|
||||||
webSocket.close(NetworkService.NORMAL_CLOSURE_STATUS, null);
|
webSocket.close(NetworkService.NORMAL_CLOSURE_STATUS, null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue