Автор оригинала: Kumar Chandrakant.
1. Обзор
В этом уроке мы рассмотрим основы взаимодействия клиент-сервер и рассмотрим это с помощью двух популярных вариантов, доступных сегодня. Мы увидим, как WebSocket, который является новым участником, конкурирует с более популярным выбором RESTful HTTP.
2. Основы сетевой коммуникации
Прежде чем мы углубимся в детали различных вариантов, их достоинства и недостатки, давайте быстро обновим ландшафт сетевого общения. Это поможет взглянуть на вещи в перспективе и лучше понять это.
Сетевые коммуникации лучше всего можно понять с точки зрения модели взаимосвязи открытых систем (OSI).
Модель OSI разделяет систему связи на семь уровней абстракции:
В верхней части этой модели находится прикладной уровень, который представляет для нас интерес в этом учебнике. Тем не менее, мы обсудим некоторые аспекты на четырех верхних уровнях, когда будем сравнивать WebSocket и RESTful HTTP.
Уровень приложений находится ближе всего к конечному пользователю и отвечает за взаимодействие с приложениями, участвующими в обмене данными. Существует несколько популярных протоколов , которые используются на этом уровне, таких как FTP, SMTP, SNMP, HTTP и WebSocket.
3. Описание WebSocket и RESTful HTTP
В то время как связь может происходить между любым количеством систем, нас особенно интересует связь между клиентом и сервером. Более конкретно, мы сосредоточимся на связи между веб-браузером и веб-сервером. Это фрейм, который мы будем использовать для сравнения WebSocket с RESTful HTTP.
Но прежде чем мы продолжим дальше, почему бы не быстро понять, что это такое!
3.1. WebSockets
Как следует из формального определения, WebSocket – это протокол связи, который обеспечивает двунаправленную полнодуплексную связь по постоянному TCP-соединению. Теперь мы подробно разберемся в каждой части этого утверждения по ходу дела.
WebSocket был стандартизирован как протокол связи IETF как RFC 6455 в 2011 году. Большинство современных веб-браузеров сегодня поддерживают протокол WebSocket.
3.2. RESTful HTTP
Хотя мы все знаем о HTTP из-за его повсеместного присутствия в Интернете, это также протокол связи прикладного уровня. HTTP-это протокол , основанный на запросе-ответе , опять же, мы лучше поймем это позже в учебнике.
REST (Representational State Transfer) – это архитектурный стиль, который накладывает ряд ограничений на HTTP для создания веб-служб.
4. Субпротокол WebSocket
В то время как WebSocket определяет протокол для двунаправленной связи между клиентом и сервером, он не ставит никаких условий для обмена сообщениями . Это остается открытым для сторон в сообщении, чтобы договориться в рамках переговоров по субпротоколу.
Не очень удобно разрабатывать подпротокол для нетривиальных приложений. К счастью, существует много популярных субпротоколов, таких как STOMP , доступных для использования . STOMP расшифровывается как Простой текстовый протокол обмена сообщениями и работает через WebSocket. Spring Boot имеет первоклассную поддержку STOMP, которую мы используем в нашем уроке.
5. Быстрая настройка в Spring Boot
Нет ничего лучше, чем увидеть рабочий пример. Итак, мы создадим простые варианты использования как в WebSocket, так и в RESTful HTTP, чтобы изучить их дальше, а затем сравнить. Давайте создадим простой серверный и клиентский компоненты для обоих.
Мы создадим простой клиент с использованием JavaScript, который отправит имя. И мы создадим сервер с использованием Java, который ответит приветствием.
5.1. WebSocket
Чтобы использовать WebSocket в Spring Boot, нам понадобится соответствующий стартер :
org.springframework.boot spring-boot-starter-websocket
Теперь мы настроим конечные точки STOMP:
@Configuration @EnableWebSocketMessageBroker public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws"); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.setApplicationDestinationPrefixes("/app"); config.enableSimpleBroker("/topic"); } }
Давайте быстро определим простой сервер WebSocket, который принимает имя и отвечает приветствием:
@Controller public class WebSocketController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(Message message) throws Exception { return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); } }
Наконец, давайте создадим клиент для связи с этим сервером WebSocket. Поскольку мы подчеркиваем связь между браузером и сервером, давайте создадим клиент на JavaScript:
var stompClient = null; function connect() { stompClient = Stomp.client('ws://localhost:8080/ws'); stompClient.connect({}, function (frame) { stompClient.subscribe('/topic/greetings', function (response) { showGreeting(JSON.parse(response.body).content); }); }); } function sendName() { stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()})); } function showGreeting(message) { $("#greetings").append(""); } " + message + "
Это завершает наш рабочий пример сервера и клиента WebSocket. В репозитории кода есть HTML-страница, которая обеспечивает простой пользовательский интерфейс для взаимодействия.
Хотя это просто царапает поверхность, WebSocket с Spring можно использовать для создания сложных чат-клиентов и многого другого.
5.2. RESTful HTTP
Сейчас мы пройдем аналогичную настройку для службы RESTful. Наш простой веб – сервис примет запрос GET с именем и ответит приветствием.
Давайте на этот раз вместо этого воспользуемся веб-стартером Spring Boot :
org.springframework.boot spring-boot-starter-web
Теперь мы определим конечную точку REST, используя мощную поддержку аннотаций, доступную весной:
@RestController @RequestMapping(path = "/rest") public class RestAPIController { @GetMapping(path="/{name}", produces = "application/json") public String getGreeting(@PathVariable("name") String name) { return "{\"greeting\" : \"Hello, " + name + "!\"}"; } }
Наконец, давайте создадим клиент на JavaScript:
var request = new XMLHttpRequest() function sendName() { request.open('GET', 'http://localhost:8080/rest/'+$("#name").val(), true) request.onload = function () { var data = JSON.parse(this.response) showGreeting(data.greeting) } request.send() } function showGreeting(message) { $("#greetings").append(""); } " + message + "
Вот и все! Опять же, в репозитории кода есть HTML-страница для работы с пользовательским интерфейсом.
Несмотря на глубокую простоту, определение REST API производственного класса может быть гораздо более сложной задачей!
6. Сравнение WebSocket и RESTful HTTP
Создав минимальные, но работающие примеры WebSocket и RESTful HTTP, мы теперь готовы понять, как они взаимодействуют друг с другом. Мы рассмотрим это с учетом нескольких критериев в следующих подразделах.
Важно отметить, что, хотя мы можем напрямую сравнивать HTTP и WebSocket, поскольку они оба являются протоколами прикладного уровня, неестественно сравнивать REST с WebSocket . Как мы видели ранее, REST-это архитектурный стиль, который использует HTTP для общения.
Следовательно, наше сравнение с WebSocket будет в основном касаться возможностей или их отсутствия в HTTP .
6.1. Схема URL-адресов
URL-адрес определяет уникальное местоположение веб-ресурса и механизм его извлечения . В связи между клиентом и сервером чаще всего мы стремимся получить статические или динамические ресурсы через связанный с ними URL-адрес.
Мы все знакомы со схемой HTTP URL:
http://localhost:8080/rest
Схема URL-адресов WebSocket также не сильно отличается:
ws://localhost:8080/ws
С самого начала единственное различие, по-видимому, заключается в символах перед двоеточием, но это многое абстрагирует от того, что происходит под капотом. Давайте исследуем дальше.
6.2. Рукопожатие
Рукопожатие относится к автоматическому способу согласования протокола связи между общающимися сторонами . HTTP является протоколом без состояния и работает в механизме запроса-ответа. При каждом HTTP-запросе устанавливается TCP-соединение с сервером через сокет.
Затем клиент ждет, пока сервер не ответит ресурсом или ошибкой. Следующий запрос от клиента повторяет все, как будто предыдущего запроса никогда не было:
WebSocket работает совсем по-другому по сравнению с HTTP и начинается с рукопожатия перед фактическим общением.
Давайте посмотрим, что представляет собой рукопожатие WebSocket:
В случае WebSocket клиент инициирует запрос на подтверждение протокола в HTTP, а затем ждет, пока сервер не ответит, принимая обновление до WebSocket с HTTP .
Конечно, поскольку рукопожатие протокола происходит по протоколу HTTP, оно следует последовательности из предыдущей диаграммы. Но как только соединение установлено, оттуда клиент и сервер переключаются на WebSocket для дальнейшей связи.
6.3. Подключение
Как мы видели в предыдущем подразделе, одно существенное различие между WebSocket и HTTP заключается в том, что WebSocket работает с постоянным TCP-соединением, в то время как HTTP создает новое TCP-соединение для каждого запроса.
Теперь очевидно, что создание нового TCP-соединения для каждого запроса не очень эффективно, и HTTP не знал об этом. Фактически, в рамках HTTP/1.1 были введены постоянные соединения, чтобы устранить этот недостаток HTTP.
Тем не менее, WebSocket был разработан с нуля для работы с постоянными TCP-соединениями .
6.4. Коммуникация
Преимущество WebSocket над HTTP-это специфический сценарий, который возникает из-за того, что клиент и сервер могут взаимодействовать способами, которые были невозможны с помощью старого доброго HTTP.
Например, в HTTP обычно клиент отправляет этот запрос, а затем сервер отвечает запрошенными данными. Для сервера не существует универсального способа связи с клиентом самостоятельно. Конечно, были разработаны шаблоны и решения, чтобы обойти это, как события, отправленные сервером (SSE), но они не были полностью естественными.
С помощью WebSocket, работающего по постоянной TCP-связи, сервер и клиент могут отправлять данные независимо друг от друга , и фактически многим взаимодействующим сторонам! Это называется двунаправленной связью.
Еще одна интересная особенность связи WebSocket заключается в том, что она является полнодуплексной . Теперь, хотя этот термин может показаться эзотерическим, он просто означает, что и сервер, и клиент могут отправлять данные одновременно . Сравните это с тем, что происходит в HTTP, когда сервер должен ждать, пока он не получит запрос в полном объеме, прежде чем он сможет ответить данными.
В то время как преимущества двунаправленной и полнодуплексной связи могут быть очевидны не сразу. мы увидим некоторые примеры использования, в которых они открывают некоторую реальную силу.
6.5. Безопасность
И последнее, но не менее важное: как HTTP, так и WebSocket используют преимущества TLS для обеспечения безопасности . В то время как HTTP предлагает https как часть своей схемы URL для использования этого, WebSocket имеет wss как часть своей схемы URL для того же эффекта.
Таким образом, защищенная версия URL-адресов из предыдущего подраздела должна выглядеть следующим образом:
https://localhost:443/rest wss://localhost:443/ws
Обеспечение безопасности как службы RESTful, так и связи WebSocket является предметом большой глубины и не может быть рассмотрено здесь. А пока давайте просто скажем, что обе стороны получают адекватную поддержку в этом отношении.
6.6. Производительность
Мы должны понимать, что WebSocket-это протокол с отслеживанием состояния, в котором связь происходит по выделенному TCP-соединению. С другой стороны, HTTP по своей сути является протоколом без состояния. Это влияет на то, как они будут работать с нагрузкой, но это действительно зависит от варианта использования.
Поскольку связь через WebSocket происходит по многоразовому TCP-соединению, накладные расходы на сообщение ниже по сравнению с HTTP . Следовательно, он может достичь более высокой пропускной способности на сервер. Но есть предел, до которого может масштабироваться один сервер, и именно здесь у WebSocket возникают проблемы. Нелегко горизонтально масштабировать приложения с помощью WebSockets.
Вот где сияет HTTP. С помощью HTTP каждый новый запрос потенциально может попасть на любой сервер. Это означает, что для увеличения общей пропускной способности мы можем легко добавить больше серверов. Это потенциально не должно влиять на приложение, работающее с HTTP.
Очевидно, что приложение само по себе может нуждаться в постоянстве состояния и сеанса, что может сделать его легче сказать, чем сделать.
7. Где Мы Должны Их Использовать?
Теперь мы достаточно насмотрелись на RESTful service через HTTP и простую связь через WebSocket, чтобы сформировать свое мнение о них. Но где и что мы должны использовать?
Важно помнить, что, хотя WebSocket появился из-за недостатков HTTP, на самом деле он не является заменой HTTP. Так что у них обоих есть свое место и свое применение. Давайте быстро поймем, как мы можем принять решение.
Для большей части сценария , где требуется случайная связь с сервером, например, получение записи сотрудника, по-прежнему разумно использовать службу REST по протоколу HTTP/S . Но для новых клиентских приложений, таких как приложение для определения цен на акции, которое требует обновлений в режиме реального времени с сервера, гораздо удобнее использовать WebSocket.
Обобщая, WebSocket больше подходит для случаев, когда связь на основе push и в режиме реального времени определяет требование более подходящим образом . Кроме того, WebSocket хорошо работает в сценариях, когда сообщение должно быть отправлено нескольким клиентам одновременно . Это те случаи, когда связь между клиентом и сервером через службы RESTful будет затруднена, если не запретительна.
Тем не менее, использование сервисов WebSocket и RESTful по протоколу HTTP должно основываться на требованиях. Как и нет серебряных пуль, мы не можем просто ожидать, что выберем одну, чтобы решить каждую проблему. Следовательно, мы должны использовать нашу мудрость в сочетании со знаниями при разработке эффективной модели коммуникации.
8. Заключение
В этом уроке мы рассмотрели основы сетевого взаимодействия с акцентом на протоколы прикладного уровня HTTP и WebSocket. Мы видели несколько быстрых демонстраций WebSocket и RESTful API по HTTP в Spring Boot.
И, наконец, мы сравнили функции протоколов HTTP и WebSocket и кратко обсудили, когда использовать каждый из них.
Как всегда, код для примеров доступен на GitHub .