Skip to content

Commit 1669f99

Browse files
committed
# Conflicts: # src/main/java/org/gridsuite/study/notification/server/NotificationWebSocketHandler.java
2 parents 8aa6e35 + 456f47f commit 1669f99

File tree

10 files changed

+217
-101
lines changed

10 files changed

+217
-101
lines changed
File renamed without changes.

lombok.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import target/configs/powsybl-build-tools.jar!powsybl-build-tools/lombok.config

pom.xml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!--
3-
43
Copyright (c) 2020, RTE (http://www.rte-france.com)
54
This Source Code Form is subject to the terms of the Mozilla Public
65
License, v. 2.0. If a copy of the MPL was not distributed with this
76
file, You can obtain one at http://mozilla.org/MPL/2.0/.
8-
97
-->
108
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
119
xmlns="http://maven.apache.org/POM/4.0.0"
@@ -15,7 +13,7 @@
1513
<parent>
1614
<groupId>com.powsybl</groupId>
1715
<artifactId>powsybl-parent-ws</artifactId>
18-
<version>11</version>
16+
<version>15</version>
1917
<relativePath/>
2018
</parent>
2119

@@ -44,7 +42,7 @@
4442
</developers>
4543

4644
<properties>
47-
<gridsuite-dependencies.version>23</gridsuite-dependencies.version>
45+
<gridsuite-dependencies.version>31</gridsuite-dependencies.version>
4846
</properties>
4947

5048
<build>
@@ -62,8 +60,8 @@
6260
<artifactId>spring-boot-maven-plugin</artifactId>
6361
</plugin>
6462
<plugin>
65-
<groupId>pl.project13.maven</groupId>
66-
<artifactId>git-commit-id-plugin</artifactId>
63+
<groupId>io.github.git-commit-id</groupId>
64+
<artifactId>git-commit-id-maven-plugin</artifactId>
6765
</plugin>
6866
</plugins>
6967
</build>
@@ -102,12 +100,17 @@
102100
<dependency>
103101
<groupId>org.projectlombok</groupId>
104102
<artifactId>lombok</artifactId>
103+
<scope>provided</scope>
105104
</dependency>
106-
107-
<!-- Runtime dependencies -->
108105
<dependency>
109106
<groupId>org.springframework.boot</groupId>
110107
<artifactId>spring-boot-starter-actuator</artifactId>
108+
</dependency>
109+
110+
<!-- Runtime dependencies -->
111+
<dependency>
112+
<groupId>io.micrometer</groupId>
113+
<artifactId>micrometer-registry-prometheus</artifactId>
111114
<scope>runtime</scope>
112115
</dependency>
113116
<dependency>

src/main/java/org/gridsuite/study/notification/server/NotificationApplication.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
*/
77
package org.gridsuite.study.notification.server;
88

9-
import com.powsybl.ws.commons.Utils;
109
import org.springframework.boot.SpringApplication;
1110
import org.springframework.boot.autoconfigure.SpringBootApplication;
1211

@@ -18,7 +17,6 @@
1817
public class NotificationApplication {
1918

2019
public static void main(String[] args) {
21-
Utils.initProperties();
2220
SpringApplication.run(NotificationApplication.class, args);
2321
}
2422
}

src/main/java/org/gridsuite/study/notification/server/NotificationWebSocketHandler.java

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,14 @@
66
*/
77
package org.gridsuite.study.notification.server;
88

9-
import java.io.UnsupportedEncodingException;
10-
import java.net.URLDecoder;
11-
import java.nio.charset.StandardCharsets;
12-
import java.time.Duration;
13-
import java.util.HashMap;
14-
import java.util.Map;
15-
import java.util.function.Consumer;
16-
import java.util.logging.Level;
17-
189
import com.fasterxml.jackson.core.JsonProcessingException;
1910
import com.fasterxml.jackson.databind.ObjectMapper;
20-
11+
import io.micrometer.core.instrument.MeterRegistry;
12+
import io.micrometer.core.instrument.MultiGauge;
13+
import io.micrometer.core.instrument.Tags;
2114
import org.gridsuite.study.notification.server.dto.Filters;
2215
import org.gridsuite.study.notification.server.dto.FiltersToAdd;
2316
import org.gridsuite.study.notification.server.dto.FiltersToRemove;
24-
import org.gridsuite.study.notification.server.dto.NetworkImpactsInfos;
2517
import org.slf4j.Logger;
2618
import org.slf4j.LoggerFactory;
2719
import org.springframework.beans.factory.annotation.Value;
@@ -37,6 +29,18 @@
3729
import reactor.core.publisher.Flux;
3830
import reactor.core.publisher.Mono;
3931

32+
import java.io.UnsupportedEncodingException;
33+
import java.net.URLDecoder;
34+
import java.nio.charset.StandardCharsets;
35+
import java.time.Duration;
36+
import java.util.HashMap;
37+
import java.util.Map;
38+
import java.util.concurrent.ConcurrentHashMap;
39+
import java.util.function.Consumer;
40+
import java.util.logging.Level;
41+
42+
import static java.util.stream.Collectors.toList;
43+
4044
/**
4145
* A WebSocketHandler that sends messages from a broker to websockets opened by clients, interleaving with pings to keep connections open.
4246
* <p>
@@ -63,36 +67,44 @@ public class NotificationWebSocketHandler implements WebSocketHandler {
6367
static final String HEADER_TIMESTAMP = "timestamp";
6468
static final String HEADER_ERROR = "error";
6569
static final String HEADER_SUBSTATIONS_IDS = "substationsIds";
66-
static final String HEADER_DELETED_EQUIPMENTS = "deletedEquipments";
6770
static final String HEADER_NODE = "node";
6871
static final String HEADER_NODES = "nodes";
6972
static final String HEADER_PARENT_NODE = "parentNode";
7073
static final String HEADER_NEW_NODE = "newNode";
7174
static final String HEADER_MOVED_NODE = "movedNode";
7275
static final String HEADER_REMOVE_CHILDREN = "removeChildren";
7376
static final String HEADER_INSERT_MODE = "insertMode";
77+
static final String HEADER_REFERENCE_NODE_UUID = "referenceNodeUuid";
78+
static final String HEADER_INDEXATION_STATUS = "indexation_status";
79+
static final String USERS_METER_NAME = "app.users";
80+
static final String USER_TAG = "user";
81+
82+
private final ObjectMapper jacksonObjectMapper;
7483
public static final String HEADER_PARAMS_NAME = "paramsName";
7584

76-
private ObjectMapper jacksonObjectMapper;
85+
private final int heartbeatInterval;
7786

78-
private int heartbeatInterval;
87+
private final Map<String, Integer> userConnections = new ConcurrentHashMap<>();
7988

80-
public NotificationWebSocketHandler(ObjectMapper jacksonObjectMapper, @Value("${notification.websocket.heartbeat.interval:30}") int heartbeatInterval) {
89+
private final MultiGauge multiGauge;
90+
91+
public NotificationWebSocketHandler(ObjectMapper jacksonObjectMapper, MeterRegistry meterRegistry, @Value("${notification.websocket.heartbeat.interval:30}") int heartbeatInterval) {
8192
this.jacksonObjectMapper = jacksonObjectMapper;
8293
this.heartbeatInterval = heartbeatInterval;
94+
this.multiGauge = MultiGauge.builder(USERS_METER_NAME).description("The current number of connections per user").register(meterRegistry);
8395
}
8496

85-
Flux<Message<NetworkImpactsInfos>> flux;
97+
Flux<Message<String>> flux;
8698

8799
@Bean
88-
public Consumer<Flux<Message<NetworkImpactsInfos>>> consumeNotification() {
100+
public Consumer<Flux<Message<String>>> consumeNotification() {
89101
return f -> {
90-
ConnectableFlux<Message<NetworkImpactsInfos>> c = f.log(CATEGORY_BROKER_INPUT, Level.FINE).publish();
102+
ConnectableFlux<Message<String>> c = f.log(CATEGORY_BROKER_INPUT, Level.FINE).publish();
91103
this.flux = c;
92104
c.connect();
93105
// Force connect 1 fake subscriber to consumme messages as they come.
94106
// Otherwise, reactorcore buffers some messages (not until the connectable flux had
95-
// at least one subscriber. Is there a better way ?
107+
// at least one subscriber). Is there a better way ?
96108
c.subscribe();
97109
};
98110
}
@@ -134,6 +146,8 @@ private static Map<String, Object> toResultHeader(Map<String, Object> messageHea
134146
passHeader(messageHeader, resHeader, HEADER_NEW_NODE);
135147
passHeader(messageHeader, resHeader, HEADER_MOVED_NODE);
136148
passHeader(messageHeader, resHeader, HEADER_USER_ID); // to filter the display of error messages in the front end
149+
passHeader(messageHeader, resHeader, HEADER_REFERENCE_NODE_UUID);
150+
passHeader(messageHeader, resHeader, HEADER_INDEXATION_STATUS);
137151
passHeader(messageHeader, resHeader, HEADER_PARAMS_NAME);
138152

139153
return resHeader;
@@ -210,10 +224,30 @@ public Mono<Void> handle(WebSocketSession webSocketSession) {
210224
webSocketSession.getAttributes().put(FILTER_UPDATE_TYPE, filterUpdateType);
211225
}
212226

213-
LOGGER.debug("New websocket connection for studyUuid={}, updateType={}", filterStudyUuid, filterUpdateType);
214227
return webSocketSession
215-
.send(notificationFlux(webSocketSession)
216-
.mergeWith(heartbeatFlux(webSocketSession)))
217-
.and(receive(webSocketSession));
228+
.send(notificationFlux(webSocketSession).mergeWith(heartbeatFlux(webSocketSession)))
229+
.and(receive(webSocketSession))
230+
.doFirst(() -> updateConnectionMetrics(webSocketSession))
231+
.doFinally(s -> updateDisconnectionMetrics(webSocketSession));
232+
}
233+
234+
private void updateConnectionMetrics(WebSocketSession webSocketSession) {
235+
var userId = webSocketSession.getHandshakeInfo().getHeaders().getFirst(HEADER_USER_ID);
236+
LOGGER.info("New websocket connection id={} for user={} studyUuid={}, updateType={}", webSocketSession.getId(), userId,
237+
webSocketSession.getAttributes().get(FILTER_STUDY_UUID), webSocketSession.getAttributes().get(FILTER_UPDATE_TYPE));
238+
userConnections.compute(userId, (k, v) -> (v == null) ? 1 : v + 1);
239+
updateConnectionMetricsRegistry();
240+
}
241+
242+
private void updateDisconnectionMetrics(WebSocketSession webSocketSession) {
243+
var userId = webSocketSession.getHandshakeInfo().getHeaders().getFirst(HEADER_USER_ID);
244+
LOGGER.info("Websocket disconnection id={} for user={}", webSocketSession.getId(), userId);
245+
userConnections.computeIfPresent(userId, (k, v) -> v > 1 ? v - 1 : null);
246+
updateConnectionMetricsRegistry();
247+
}
248+
249+
private void updateConnectionMetricsRegistry() {
250+
multiGauge.register(userConnections.entrySet().stream().map(e -> MultiGauge.Row.of(Tags.of(USER_TAG, e.getKey()), e.getValue()))
251+
.collect(toList()), true);
218252
}
219253
}

src/main/java/org/gridsuite/study/notification/server/dto/EquipmentDeletionInfos.java

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/main/java/org/gridsuite/study/notification/server/dto/NetworkImpactsInfos.java

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)