Статьи

Хвост файла — пример Spring Websocket


Это пример, который я хотел попробовать на какое-то время — приложение Websocket, которое следит за содержимым файла.

Ниже приведен окончательный вид веб-приложения. Это приложение
состоит из нескольких частей:

Генерация файла на хвост:

Я решил использовать набор из 100 случайных кавычек в качестве источника содержимого файла, каждые несколько секунд приложение генерирует цитату и записывает эту цитату во временный файл. 
Spring Integration  используется для подключения этого потока для записи содержимого в файл:

<int:channel id="toFileChannel"/>

<int:inbound-channel-adapter ref="randomQuoteGenerator" method="generateQuote" channel="toFileChannel">
	<int:poller fixed-delay="2000"/>
</int:inbound-channel-adapter>

<int:chain input-channel="toFileChannel">
	<int:header-enricher>
		<int:header name="file_name" value="quotes.txt"/>
	</int:header-enricher>
	<int-file:outbound-channel-adapter directory="#{systemProperties['java.io.tmpdir']}" mode="APPEND" />
</int:chain>

Вкратце, потоки Spring Integration теперь можно записывать с использованием DSL на основе Java, и этот поток с использованием Java доступен 
здесь.

Завершение файла и отправка контента брокеру

Фактическая привязка самого файла может быть выполнена с помощью специальной команды хвоста ОС или с использованием библиотеки, подобной 
Apache Commons IO . Опять же, в моем случае я решил использовать Spring Integration, которая предоставляет адаптеры входящего канала для привязки файла исключительно с использованием конфигурации, этот поток выглядит следующим образом:

<int:channel id="toTopicChannel"/>

<int-file:tail-inbound-channel-adapter id="fileInboundChannelAdapter"
				channel="toTopicChannel"
				file="#{systemProperties['java.io.tmpdir']}/quotes.txt"
				delay="2000"
				file-delay="10000"/>

<int:outbound-channel-adapter ref="fileContentRecordingService" method="sendLinesToTopic" channel="toTopicChannel"/>

и его рабочий 
Java-эквивалент.

Существует ссылка на «fileContentRecordingService» выше, это компонент, который направит строки файла в место, на которое будет подписан клиент Websocket.

Конфигурация сервера Websocket

Поддержка Spring Websocket  упрощает написание приложения на основе Websocket, в этом случае вся рабочая конфигурация выглядит следующим образом:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketDefaultConfig extends AbstractWebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
//config.enableStompBrokerRelay("/topic/", "/queue/");
config.enableSimpleBroker("/topic/", "/queue/");
config.setApplicationDestinationPrefixes("/app");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/tailfilesep").withSockJS();
}
}

Это может показаться немного чрезмерным, но то, что делают эти несколько строк конфигурации, является очень мощным, и конфигурацию можно лучше понять, пройдя по 
ссылке здесь . Вкратце, он устанавливает конечную точку веб-сокета в ‘/ tailfileep’ uri, эта конечная точка улучшена 
 поддержкой
SockJS , Stomp используется в качестве суб-протокола, конечные точки `/ topic` и` / queue` настроены для реального брокера, такого как RabbitMQ или ActiveMQ, но только в памяти.

Возвращаясь к «fileContentRecordingService» еще раз, этот компонент, по сути, берет строку файла и отправляет ее этому посреднику в памяти, SimpMessagingTemplate облегчает эту проводку:

public class FileContentRecordingService {
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;

public void sendLinesToTopic(String line) {
this.simpMessagingTemplate.convertAndSend("/topic/tailfiles", line);
}
}

Настройка веб-сокета

Пользовательский интерфейс 
основан  на angularjs , клиентский контроллер настроен таким образом и внутренне использует библиотеки javascript для поддержки 
sockjs  и 
stomp  :

var tailFilesApp = angular.module("tailFilesApp",[]);

tailFilesApp.controller("TailFilesCtrl", function ($scope) {
    function init() {
        $scope.buffer = new CircularBuffer(20);
    }

    $scope.initSockets = function() {
        $scope.socket={};
        $scope.socket.client = new SockJS("/tailfilesep);
        $scope.socket.stomp = Stomp.over($scope.socket.client);
        $scope.socket.stomp.connect({}, function() {
            $scope.socket.stomp.subscribe("/topic/tailfiles", $scope.notify);
        });
        $scope.socket.client.onclose = $scope.reconnect;
    };

    $scope.notify = function(message) {
        $scope.$apply(function() {
            $scope.buffer.add(angular.fromJson(message.body));
        });
    };

    $scope.reconnect = function() {
        setTimeout($scope.initSockets, 10000);
    };

    init();
    $scope.initSockets();
});

Основой этого кода является функция «уведомить», которая обратный вызов действует на сообщения от сервера, в этом случае новые строки, поступающие в файл и отображающие его в текстовой области.

Это оборачивает все приложение для создания файла. Полный рабочий образец без каких-либо внешних зависимостей доступен в 
этом месте github , инструкции по его запуску также доступны в этом месте.

Вывод

Spring Websockets предоставляет краткий способ создания приложений на основе Websocket, этот пример демонстрирует эту поддержку. Я недавно выступал по этой теме на своем местном JUG (
IndyJUG ), и колода с презентацией доступна 
здесь.