1. Обзор
В этом руководстве мы рассмотрим основы взаимодействия клиент-сервер и рассмотрим два популярных варианта, доступных сегодня. Мы увидим, как WebSocket, который является новым участником, справляется с более популярным выбором RESTful HTTP.
2. Основы сетевого общения
Прежде чем мы углубимся в детали различных вариантов, их достоинств и недостатков, давайте быстро освежим представление о сетевых коммуникациях. Это поможет взглянуть на вещи в перспективе и лучше понять это.
Сетевые коммуникации можно лучше всего понять с точки зрения модели взаимодействия открытых систем (OSI) .
Модель OSI разделяет систему связи на семь уровней абстракции:
В верхней части этой модели находится прикладной уровень, который представляет для нас интерес в этом руководстве. Однако мы обсудим некоторые аспекты четырех верхних уровней по мере того, как будем сравнивать WebSocket и RESTful HTTP.
Прикладной уровень находится ближе всего к конечному пользователю и отвечает за взаимодействие с приложениями, участвующими в обмене данными. На этом уровне используются несколько популярных протоколов , таких как FTP, SMTP, SNMP, HTTP и WebSocket.
3. Описание WebSocket и RESTful HTTP
Хотя связь может происходить между любым количеством систем, нас особенно интересует связь клиент-сервер. В частности, мы сосредоточимся на связи между веб-браузером и веб-сервером. Это кадр, который мы будем использовать для сравнения WebSocket с RESTful HTTP.
Но прежде чем мы продолжим, почему бы не понять, что они из себя представляют!
3.1. Веб-сокеты
Согласно формальному определению, WebSocket — это коммуникационный протокол, обеспечивающий двунаправленную полнодуплексную связь через постоянное TCP-соединение. Теперь мы подробно разберем каждую часть этого утверждения по ходу дела.
WebSocket был стандартизирован как протокол связи IETF как RFC 6455 в 2011 году. Сегодня большинство современных веб-браузеров поддерживают протокол WebSocket.
3.2. RESTful HTTP
Хотя мы все знаем о HTTP из-за его повсеместного присутствия в Интернете, он также является протоколом связи прикладного уровня. HTTP — это протокол, основанный на запросе-ответе , опять же, мы лучше поймем это позже в этом руководстве.
REST (передача репрезентативного состояния) — это архитектурный стиль, который накладывает ряд ограничений на HTTP для создания веб-сервисов.
4. Подпротокол WebSocket
Хотя WebSocket определяет протокол для двунаправленной связи между клиентом и сервером, он не ставит никаких условий для обмена сообщениями . Это остается открытым для сторон в общении, чтобы согласиться в рамках переговоров по подпротоколу.
Неудобно разрабатывать подпротокол для нетривиальных приложений. К счастью, есть много популярных подпротоколов, таких как STOMP , доступных для использования . STOMP расшифровывается как Simple Text Oriented Messaging Protocol и работает через WebSocket. Spring Boot имеет первоклассную поддержку STOMP, которую мы будем использовать в нашем руководстве.
5. Быстрая настройка в Spring Boot
Нет ничего лучше, чем увидеть работающий пример. Итак, мы создадим простые варианты использования как в WebSocket, так и в RESTful HTTP, чтобы изучить их глубже, а затем сравнить. Давайте создадим простой серверный и клиентский компоненты для обоих.
Мы создадим простой клиент с использованием JavaScript, который будет отправлять имя. И мы создадим сервер с использованием Java, который ответит приветствием.
5.1. Веб-сокет
Чтобы использовать WebSocket в Spring Boot, нам понадобится соответствующий стартер :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Теперь мы настроим конечные точки 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("<tr><td>" + message + "</td></tr>");
}
На этом наш рабочий пример сервера и клиента WebSocket завершен. В репозитории кода есть HTML-страница, которая обеспечивает простой пользовательский интерфейс для взаимодействия.
Хотя это только верхушка, WebSocket с Spring можно использовать для создания сложных чат-клиентов и многого другого.
5.2. RESTful HTTP
Сейчас мы пройдем аналогичную настройку службы RESTful. Наш простой веб-сервис примет запрос GET с именем и ответит приветствием.
Давайте на этот раз воспользуемся веб-стартером Spring Boot :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Теперь мы определим конечную точку REST, используя мощную поддержку аннотаций, доступную в Spring:
@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("<tr><td>" + message + "</td></tr>");
}
Вот и все! Опять же, в репозитории кода есть 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 клиент инициирует запрос Protocol Handshake в 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 обычно клиент отправляет этот запрос, а затем сервер отвечает запрошенными данными. Для сервера не существует универсального способа общаться с клиентом самостоятельно. Конечно, для обхода этого были разработаны шаблоны и решения, такие как Server-Sent Events (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 через 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 .