Naszła mnie potrzeba aby utworzyć pewien dashboard, który reagowałby na zmiany przychodzące z serwera. Jako, że tworzę swoje aplikacje z wykorzystaniem Spring Boot (backend) i AngularJS (frontend) to o komunikacje serwer -> klient należy zadbać samemu. Po pospiesznej analizie tematu okazało się, że taką komunikację zapewnia protokół WebSockets (co dla niektórych może jest oczywiste, lecz dla mnie nie było). Co trzeba zrobić? Zestawić połączenie pomiędzy serwerem, a klientem i przesyłać wiadomości. Proste? Proste. No to jazda.
Potrzebne jeszcze jest coś co będzie nasłuchiwać na generowane eventy i przysyłać komunikaty przez websocket.
UWAGA. Poszukując informacji w internecie często napotykałem na tutoriale wykorzystujące adnotację
Projekt
Kombinacja Spring Boot i AngularJS (angular w wersji 1) tak mi się spodobała, że utworzyłem sobie własny archetyp mavena, aby szybko móc tworzyć kolejne proste aplikacje. Jest on dostępny na moim GitHubie. Instrukcje tworzenia z niego projektu są w Readme, nie będę się powtarzał.Eventy
Taka aktualizacja danych musi być wywoływana przez jakieś zdarzenie. Na potrzeby przykładu wykorzystałem springowe eventy. Eventem w springu może być zwykłe POJO. W moim przypadku mam klasęMyEvent
z atrybutami timestamp i message. public class MyEvent {
private final long timestamp = System.currentTimeMillis();
private final String message;
public MyEvent(String message) {
this.message = message;
}
public long getTimestamp() {
return timestamp;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return "MyEvent{" +
"timestamp=" + timestamp +
", message='" + message + '\'' +
'}';
}
}
Warto by jeszcze te eventy jakoś produkować. Do tego wykorzystałem springowy sheduler. Pozwala on wykonywać pewną operację regularnie z określoną częstotliwością. W moim przypadku jest to publikowanie nowego eventu co 5 sekund. Publikować eventy pozwala bean klasy ApplicationEventPublisher
.
@Component
@EnableScheduling
public class MyEventProducer {
private final ApplicationEventPublisher publisher;
@Autowired
public MyEventProducer(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Scheduled(fixedRate = 5000)
public void publishMyEvent() {
publisher.publishEvent(new MyEvent("hello"));
}
}
WebSockets - Spring
Uruchomienie obsługi websocketów w boocie wymaga dodania odpowiedniej zależności:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Następnie należy skonfigurować wbudowany w kontener broker komunikatów.@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
}
Endpoint /chat
jest endpointem, za pomocą którego należy nawiązywać połączenie z brokerem. /topic
określa prefix, którym należy poprzedzać endpoint komunikatów.Potrzebne jeszcze jest coś co będzie nasłuchiwać na generowane eventy i przysyłać komunikaty przez websocket.
@Component
public class WebSocketSender {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketSender.class);
@Autowired
private SimpMessagingTemplate template;
@EventListener
public void send(MyEvent myEvent) {
LOGGER.info(myEvent.toString());
template.convertAndSend("/topic/myevents", myEvent);
}
}
Wysyłkę komunikatów realizuję przez template. Wiadomości wysyłane są jako org.springframework.messaging.Message
więc można skonstruować ten obiekt samemu, a można wykorzystać metodę konwertującą.UWAGA. Poszukując informacji w internecie często napotykałem na tutoriale wykorzystujące adnotację
@SendTo
jednak zawsze była ona wykorzystana razem z adnotacją @MessageMapping
. Z informacji, które udało mi się uzyskać (dokumentacja tego nie opisuje), @SendTo
publikuje komunikaty tylko gdy metoda jest wywoływana przez obsługę komunikatu (tzn. jest wywołana przez komunikat z websocketu).WebSockets - AngularJS
Potrzebujemy klienta do websocketa. Jak to w angularze, oczywiście jest odpowiedni plugin.<dependency>
<groupId>org.webjars.bower</groupId>
<artifactId>ng-stomp</artifactId>
<version>0.4.0</version>
</dependency>
Linkujemy odpowiedni skrypt:
<script src="webjars/ng-stomp/0.4.0/dist/ng-stomp.standalone.min.js"></script>
Rejestrujemy plugin:
angular.module('app', ['ngStomp'])
Za pomocą zależności $stomp
łączymy się do endpointa '/chat'
aby ustanowić połączenie i subskrybujemy na odpowiednie komunikaty. Opcjonalnie można włączyć sobie tryb debugowania aby w konsoli przeglądarki mieć podgląd przepływu danych.
$stomp.setDebug(function (args) {
$log.debug(args);
});
$stomp
.connect('/chat')
.then(function (frame) {
var subscription = $stomp.subscribe('/topic/myevents', function (payload, headers, res) {
handler(payload);
});
});
Odświeżanie widoku wykonuję za pomocą metody $apply
obiektu $scope
, która aktualizuje zbindowaną zmienną w kontrolerze. Obsługę tę należy przekazać jako handler do subskrypcji.
$scope.$apply(function () {
vm.message = message;
});
Ten komentarz został usunięty przez administratora bloga.
OdpowiedzUsuń