Mojolicious повторное использование ранее установленного соединения

Я пытаюсь повторно использовать ранее установленное соединение веб-сокета, чтобы избежать рукопожатия веб-сокета. Я обнаружил, что пользовательская транзакция websocket может быть построена с использованием build_websocket_tx (подробнее здесь), и есть идентификатор соединения для каждого соединения через веб-сокет, который можно получить с помощью connection подпрограмма, определенная в Mojo::Transaction (подробнее здесь). Могу ли я как-то объединить оба из них, чтобы повторно использовать соединение? Есть ли другой способ сделать это?

PS: соединения Websocket должны быть последовательными и многоразовыми. Но Mojolicoious не предоставляет таких опций для веб-сокетов.

РЕДАКТИРОВАТЬ

Пример кода без повторного использования соединения.

#!/usr/bin/perl

use strict;
use warnings;

use Mojo::UserAgent;
use JSON qw |encode_json|;

my $ua = Mojo::UserAgent->new;
my $url = "wss://trello.com/1/Session/socket";

$| = 1;

sub _connect {
    my $req = {
        type => "ping",
        reqid=> 0
    };
    $ua->websocket(
        $url => sub {
            my ($ua, $tx) = @_;

            die "error: ", $tx->res->error->{message}, "\n" if $tx->res->error;
            die 'Not a websocket connection' unless $tx->is_websocket;

            # Connection established.
            $tx->on(
                message => sub {
                    my ($tx, $msg) = @_;
                    print "$msg\n";
                    $tx->closed; #Close connection
                });
            $tx->send(encode_json($req));
        });
}

sub reuse_conn {
    # Re use connection
}

_connect();

Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

1 ответ

Предварительные условия: для запуска клиентского скрипта Mojolicious с отладочной информацией:

MOJO_EVENTEMITTER_DEBUG=1 MOJO_USERAGENT_DEBUG=1 perl mua.pl

Как и в версии 7.43, Mojo::UserAgent имеет встроенный пул соединений, но специально отказывается использовать его для WebSockets. Это может быть связано с тем, что, как вы сказали, WebSockets находится в состоянии с состоянием, и если UserAgent слепо повторно использует соединения, это может вызвать хаос. Однако, если ваше приложение знает, как их безопасно использовать, это другое дело.

Этот вопрос недавно возник в канале IRC для Mojolicious, и Шри, автор, сказал:

16:28 sri mohawk: некоторые фреймворки, такие как phoenix, имеют собственный протокол более высокого уровня поверх веб-сокетов для мультиплексирования нескольких каналов https://hexdocs.pm/phoenix/channels.html
16:28 звучит так, как ты и хочешь
16:28 у mojolicious должно быть что-то подобное, но пока нет
[...]
16:42 Шри не сложно построить на вершине милосердия, но сейчас вы должны сделать это сами
16:42 в конечном счете, я бы надеялся, что у нас это будет в ядре, без части шины сообщений
16:43 но управление каналом и маршрутизация
[...]
16:50   jberger mohawk Я написал Mojolicious::Plugin::Multiplex, который может помочь
16:51 Для примера инструмента более высокого уровня

Я признаю, что ОП сказал:

Единственный способ, которым я смог повторно использовать соединение, - это сохранить объект транзакции и использовать его при последующих вызовах.

Однако, поскольку код в настоящее время, кажется, это единственный путь. Этот код демонстрирует, как создать, поддерживать и использовать свой собственный пул соединений:

#!/usr/bin/perl

use strict;
use warnings;
use Mojo::UserAgent;
use Time::HiRes qw(time);
$| = 1;
my $REQ = {
    type => "ping",
    reqid => 0,
};
my $URL = "wss://trello.com/1/Session/socket";
my $SECONDS = 2;
my $POOL_SIZE = 5;

my $ua = Mojo::UserAgent->new;
my @pool;

sub make_conn {
  my ($ua, $url, $pool) = @_;
  $ua->websocket($URL => sub {
    my (undef, $tx) = @_;
    die "error: ", $tx->res->error->{message}, "\n" if $tx->res->error;
    die 'Not a websocket connection' unless $tx->is_websocket;
    push @$pool, $tx;
  });
}

# pool gets pushed onto, shifted off, so using recently-used connection
sub send_message {
  my ($pool, $request, $start) = @_;
  my $tx = shift @$pool;
  die "got bad connection" unless $tx; # error checking needs improving
  $tx->once(message => sub {
    my (undef, $msg) = @_;
    print "got back: $msg\n";
print "took: ", time - $start, "\n";
    push @$pool, $tx;
  });
  $tx->send({json => $request});
}

make_conn($ua, $URL, \@pool) for (1..5); # establish pool

# every 2 secs, send a message
my $timer_cb;
$timer_cb = sub {
  my $loop = shift;
  print "every $SECONDS\n";
  send_message(\@pool, $REQ, time);
  $loop->timer($SECONDS => $timer_cb);
};
Mojo::IOLoop->timer($SECONDS => $timer_cb);

Mojo::IOLoop->start unless Mojo::IOLoop->is_running;

На высоком уровне это работает так:

  • сделать 5 подключений в бассейне
  • send_message использует наименее недавно использованное соединение в этом пуле
  • отправлять сообщение каждые две секунды, регистрируя одноразовый обратный вызов "по сообщению" для обработки ответа

Для простоты он не проверяет, что соединения все еще работают, когда получает их из пула, и использует первую двухсекундную задержку для инициализации всех 5 соединений.

Использование time звонки демонстрирует увеличение скорости от использования этого пула. Ваш предоставленный код занимает (в моей системе) около 300 мс, чтобы установить соединение, затем отправить и получить. Использование пула занимает около 120 мс.

Другие вопросы по тегам