Skip to content

Commit

Permalink
Implement NetworkStatisticsService
Browse files Browse the repository at this point in the history
The NetworkStatisticsService is an attempt to decouple the network
statistic computation from the UI thread. Here, the
NetworkStatisticsService schedules repeating tasks on a
ScheduledExecutorService.
  • Loading branch information
alvasw committed Feb 15, 2023
1 parent b482276 commit aad2273
Show file tree
Hide file tree
Showing 6 changed files with 777 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network.p2p.network.statistics;

import bisq.common.proto.network.NetworkEnvelope;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import lombok.Getter;
import lombok.Setter;

@Getter
public class ConnectionStatistics {

public interface Listener {
void onNewSentBytes(long numberOfNewBytes);

void onNewReceivedBytes(long numberOfNewBytes);

void onAddSentMessage(NetworkEnvelope networkEnvelope);

void onAddReceivedMessage(NetworkEnvelope networkEnvelope);
}

private final Date creationDate = new Date();
private final List<Listener> allListeners = new ArrayList<>();
private final Map<String, Integer> receivedMessages = new HashMap<>();
private final Map<String, Integer> sentMessages = new HashMap<>();

private long lastActivityTimestamp = System.currentTimeMillis();
private long sentBytes;
private long receivedBytes;
private int totalSentMessages;
private int totalReceivedMessages;
@Setter
private int roundTripTime;

public void addListener(Listener listener) {
allListeners.add(listener);
}

public void removeListener(Listener listener) {
allListeners.remove(listener);
}

void updateLastActivityTimestamp() {
lastActivityTimestamp = System.currentTimeMillis();
}

void addSentBytes(int value) {
sentBytes += value;
allListeners.forEach(listener -> listener.onNewSentBytes(value));
}

void addReceivedBytes(int value) {
receivedBytes += value;
allListeners.forEach(listener -> listener.onNewReceivedBytes(value));
}

void addSentMessage(NetworkEnvelope networkEnvelope) {
String messageClassName = networkEnvelope.getClass().getSimpleName();
sentMessages.merge(messageClassName, 1, Integer::sum);

totalSentMessages++;
allListeners.forEach(listener -> listener.onAddSentMessage(networkEnvelope));
}

void addReceivedMessage(NetworkEnvelope networkEnvelope) {
String messageClassName = networkEnvelope.getClass().getSimpleName();
receivedMessages.merge(messageClassName, 1, Integer::sum);

totalReceivedMessages++;
allListeners.forEach(listener -> listener.onAddReceivedMessage(networkEnvelope));
}

public long getLastActivityAge() {
return System.currentTimeMillis() - lastActivityTimestamp;
}

@Override
public String toString() {
return "ConnectionStatistics{" +
"\n creationDate=" + creationDate +
",\n lastActivityTimestamp=" + lastActivityTimestamp +
",\n sentBytes=" + sentBytes +
",\n receivedBytes=" + receivedBytes +
",\n receivedMessages=" + receivedMessages +
",\n sentMessages=" + sentMessages +
",\n roundTripTime=" + roundTripTime +
"\n}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network.p2p.network.statistics;

import java.util.List;

import lombok.Getter;

@Getter
public class ConnectionStatsAccumulator implements Runnable {
private final long startTime = System.currentTimeMillis();

private final List<ConnectionStatistics> allConnectionStatistics;

private long totalSentBytes;
private long totalReceivedBytes;

private int totalSentMessages;
private int totalReceivedMessages;

private double totalSentMessagesPerSec;
private double totalReceivedMessagesPerSec;

private double totalSentBytesPerSec;
private double totalReceivedBytesPerSec;

public ConnectionStatsAccumulator(List<ConnectionStatistics> allConnectionStatistics) {
this.allConnectionStatistics = allConnectionStatistics;
}

@Override
public void run() {
long totalSentBytes = 0;
long totalReceivedBytes = 0;

int totalSentMessages = 0;
int totalReceivedMessages = 0;

for (ConnectionStatistics statistic : allConnectionStatistics) {
totalSentBytes += statistic.getSentBytes();
totalReceivedBytes += statistic.getReceivedBytes();

totalSentMessages += statistic.getTotalSentMessages();
totalReceivedMessages += statistic.getTotalReceivedMessages();
}

this.totalSentBytes = totalSentBytes;
this.totalReceivedBytes = totalReceivedBytes;

this.totalSentMessages = totalSentMessages;
this.totalReceivedMessages = totalReceivedMessages;

long passed = (System.currentTimeMillis() - startTime) / 1000;
totalSentMessagesPerSec = ((double) totalSentMessages / passed);
totalReceivedMessagesPerSec = ((double) totalReceivedMessages) / passed;

totalSentBytesPerSec = ((double) totalSentBytes) / passed;
totalReceivedBytesPerSec = ((double) totalReceivedBytes) / passed;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network.p2p.network.statistics;

public interface NetworkStatistics {
long getTotalSentBytes();
long getTotalReceivedBytes();
int getTotalSentMessages();
int getTotalReceivedMessages();
double getTotalSentMessagesPerSec();
double getTotalReceivedMessagesPerSec();
double getTotalSentBytesPerSec();
double getTotalReceivedBytesPerSec();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.network.p2p.network.statistics;

import bisq.common.util.Utilities;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Getter
public class NetworkStatisticsService implements NetworkStatistics {

private final long startTime = System.currentTimeMillis();

private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
private final List<ConnectionStatistics> allConnectionStatistics = new CopyOnWriteArrayList<>();
private final ConnectionStatsAccumulator connectionStatsAccumulator =
new ConnectionStatsAccumulator(allConnectionStatistics);

private final Map<String, Integer> totalSentMessages = new HashMap<>();
private final Map<String, Integer> totalReceivedMessages = new HashMap<>();

public ConnectionStatistics newConnectionStatistics() {
var connectionStatistics = new ConnectionStatistics();
allConnectionStatistics.add(connectionStatistics);
return connectionStatistics;
}

public void start() {
scheduledExecutorService.scheduleAtFixedRate(
connectionStatsAccumulator, 1, 1, TimeUnit.SECONDS
);
scheduledExecutorService.scheduleAtFixedRate(
createStatisticsLogger(), 1, 1, TimeUnit.HOURS
);
}

public void shutdown() {
scheduledExecutorService.shutdownNow();
}

private Runnable createStatisticsLogger() {
return () -> {
ConnectionStatsAccumulator allStats = connectionStatsAccumulator;
String ls = System.lineSeparator();

log.info("Accumulated network statistics:" + ls +
"Bytes sent: {};" + ls +
"Number of sent messages/Sent messages: {} / {};" + ls +
"Number of sent messages per sec: {};" + ls +
"Bytes received: {}" + ls +
"Number of received messages/Received messages: {} / {};" + ls +
"Number of received messages per sec: {}" + ls,
Utilities.readableFileSize(allStats.getTotalSentBytes()),
allStats.getTotalSentMessages(), totalSentMessages,
allStats.getTotalSentMessagesPerSec(),
Utilities.readableFileSize(allStats.getTotalReceivedBytes()),
allStats.getTotalReceivedMessages(), totalReceivedMessages,
allStats.getTotalReceivedMessagesPerSec());
};
}

@Override
public long getTotalSentBytes() {
return connectionStatsAccumulator.getTotalSentBytes();
}

@Override
public long getTotalReceivedBytes() {
return connectionStatsAccumulator.getTotalReceivedBytes();
}

@Override
public double getTotalSentMessagesPerSec() {
return connectionStatsAccumulator.getTotalSentMessagesPerSec();
}

@Override
public double getTotalReceivedMessagesPerSec() {
return connectionStatsAccumulator.getTotalReceivedMessagesPerSec();
}

@Override
public double getTotalSentBytesPerSec() {
return connectionStatsAccumulator.getTotalSentBytesPerSec();
}

@Override
public double getTotalReceivedBytesPerSec() {
return connectionStatsAccumulator.getTotalReceivedBytesPerSec();
}
}
Loading

0 comments on commit aad2273

Please sign in to comment.