Как ускорить передачу данных между клиентом и сервером с помощью веб-сокетов?
В настоящее время я использую веб-сокеты, чтобы отправить изображение на мой сервер, обработать его, а затем отправить обратно. В частности, я использую Ruby с Sinatra и sinatra-websocket.
На моем сервере разработки требуется ~2 секунды, чтобы отправить изображение на сервер и получить точное изображение без обработки.
В экземпляре AWS-EC2 это занимает ~15 секунд. Файл изображения, который я отправляю, составляет ~500 КБ. Моя скорость загрузки и скачивания намного выше.
Как я могу ускорить этот процесс? Это наивный способ отправки изображений туда и обратно?
Изменить: чтобы повторить мою проблему, вы можете клонировать и запустить мое репо в бесплатном экземпляре уровня AWS-EC2.
1 ответ
Это скорее помощник по тестированию, чем ответ, поскольку в вопросе недостаточно подробностей, чтобы сузить проблему.
Я написал простое тестовое приложение, используя Plezi. Так как я автор плези, мне было легче, чем выучить свой стек.
Он отлично работает на моем компьютере, 79 мс для файла ~ 3 Мб.
Обход ~3Mb к Heroku занял у моей системы 3 секунды и уменьшился до ~2,5 секунд после завершения прогрева TCP/IP...
... но мои скорости интернета, вероятно, влияют на тест (мой прием в настоящее время низкий, поэтому я могу быть медленным).
Я не уверен, что смогу воспроизвести проблему, но вы можете использовать код в этом ответе, чтобы протестировать свой сервер.
Если поездка туда и обратно занимает больше 10 секунд, это может быть стек EC2. Я не думаю, что 10 секунд было бы разумно для ~500Kb.
С другой стороны, если это более короткое и обратно, это может быть способ тестирования приложения или стека Ruby... в этом случае, возможно, решение состоит в том, чтобы переключиться на plezi (или собственный дизайн веб-сокета с йодом).
Вы можете вставить следующий код в config.ru
(помните, вам также понадобится гем-файл с plezi
драгоценный камень и, возможно, Gemfile.lock
):
# The roundtrip html client
ROUNDTRIP_CLIENT = <<CLIENT_EFO
<html>
<head>
<script src = '/client.js'></script>
</head>
<body>
<input type='file' id='test_f' lable='file to upload'></input>
<button id='test_b'>run test</button>
<div id='output'></div>
<script>
var client;
window.onload = function (e) {
client = new PleziClient();
client.autoreconnect = true;
client.roundtrip = (e) => {
var d = new Date();
e.completed_at = d.getTime();
console.log(e);
document.getElementById('output').innerHTML += "<p>Test for " +
"<a href='" + e.data + "' target='_blank'>" + Math.round(e.data.length / 1024)+ "Kb encoded file</a>" +
" completed in " + (e.completed_at - e.time) + "ms</p>";
}
client.onopen = (e) => console.log("Websocket client open", e);
}
function run_test(e) {
console.log("File submitted.");
reader = new FileReader();
reader.onloadend = function(e)
{
console.log("File loaded, " + e.target.result.length + "bytes... starting test.")
var d = new Date();
client.emit({event: "roundtrip", data: e.target.result, time: d.getTime() });
}
reader.readAsDataURL(document.getElementById('test_f').files[0]);
return false;
}
document.getElementById('test_b').onclick = run_test;
</script>
</body>
</html>
CLIENT_EFO
# require plezi
require 'plezi'
# For security, Iodine limists websocket messages.
# We update the default limit from ~250Kb to ~4Mb.
# This replaces the commandline option: iodine -v -maxms 4194304
Iodine::Rack.max_msg_size = 4194304
# the roundtrip controller... quite simple.
class RoundTrip
# return the roundtrip client.
def index
ROUNDTRIP_CLIENT
end
# echo back the websocket message - we're just testing the round trip.
def on_message data
write data
end
end
# Set the plezi root route to the RoundTrip controller
Plezi.route '/', RoundTrip
# Set the client javascript route - I'm using it as a heler.
Plezi.route '/client.js', :client
# Set Rack to run the Plezi application
run Plezi.app
Чтобы запустить код из терминала, используйте iodine
команда (это запустит iodine
сервер, который требует Plezi.
РЕДАКТИРОВАТЬ
По ссылке на git-repo (в комментариях) я понял, что JSON анализируется сервером, а затем переиздается.
Чтобы подражать этому, я обновил пример кода.
Это должно быть похоже на то, что, по-видимому, выполняет код в репозитории, и это добавляет некоторое время к циклу, так как анализ и переформатирование JSON создают копии данных, что требует выделения памяти, а также времени ЦП.
Единственное изменение в коде в RoundTrip
класс контроллера, но я вставляю все это для вашего удобства копирования + вставки.
Поместите следующий код в свой app.rb
файл (не забудьте отредактировать install.sh
установить plezi
драгоценный камень):
# The roundtrip html client
ROUNDTRIP_CLIENT = <<CLIENT_EFO
<html>
<head>
<script src = '/client.js'></script>
</head>
<body>
<input type='file' id='test_f' lable='file to upload'></input>
<button id='test_b'>run test</button>
<div id='output'></div>
<script>
var client;
window.onload = function (e) {
client = new PleziClient();
client.autoreconnect = true;
client.roundtrip = (e) => {
var d = new Date();
e.completed_at = d.getTime();
console.log(e);
document.getElementById('output').innerHTML += "<p>Test for " +
"<a href='" + e.data + "' target='_blank'>" + Math.round(e.data.length / 1024)+ "Kb encoded file</a>" +
" completed in " + (e.completed_at - e.time) + "ms</p>";
}
client.onopen = (e) => console.log("Websocket client open", e);
}
function run_test(e) {
console.log("File submitted.");
reader = new FileReader();
reader.onloadend = function(e)
{
console.log("File loaded, " + e.target.result.length + "bytes... starting test.")
var d = new Date();
client.emit({event: "roundtrip", data: e.target.result, time: d.getTime() });
}
reader.readAsDataURL(document.getElementById('test_f').files[0]);
return false;
}
document.getElementById('test_b').onclick = run_test;
</script>
</body>
</html>
CLIENT_EFO
# require plezi
require 'plezi'
# For security, Iodine limists websocket messages.
# We update the default limit from ~250Kb to ~4Mb.
# This replaces the commandline option: iodine -v -maxms 4194304
Iodine::Rack.max_msg_size = 4194304
# the roundtirp controller... quite simple.
class RoundTrip
@auto_dispatch = true
# return the roundtrip client.
def index
ROUNDTRIP_CLIENT
end
# Using Auto-Dispatch, the JSON is parsed and this event is invoked.
def roundtrip msg
# Hash results are automatically converted into JSON and emitted
msg
end
end
# Set the plezi root route to the RoundTrip controller
Plezi.route '/', RoundTrip
# Set the client javascript route - I'm using it as a heler.
Plezi.route '/client.js', :client
# Plezi will start automatically when the script exits
Чтобы запустить код из терминала, используйте ruby app.rb
Команда, так же, как ваш репо для существующего приложения.
В то время как старый код просто предлагал ответ "эхо", новый код (который выглядит почти так же) имеет еще несколько шагов:
Используя Auto-Dispatch, платформа Plezi теперь автоматически анализирует JSON и маршрутизирует событие ("туда-обратно") метода контроллера (
roundtrip
).Метод получает хэш с проанализированными данными и возвращает этот хэш обратно в Plezi.
Фреймворк собирает Hash, форматирует объект JSON и выдает результат (не String или Hash игнорируются)...
... это похоже на поведение репо.