Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async best practice question #84

Open
LBanfalvi opened this issue Sep 8, 2019 · 4 comments
Open

Async best practice question #84

LBanfalvi opened this issue Sep 8, 2019 · 4 comments

Comments

@LBanfalvi
Copy link

LBanfalvi commented Sep 8, 2019

Hi!

We have an async socket server written in ReactPHP. In some cases we would like to forward the processed packages to an AMQP server. Can you tell me what is the best practice to publish a message in this case? Currently we build up an async connection when the server starts, and in the onConnection event handler we connect to a channel, publish a message and then we close the channel. I don't think this is the proper way...

  $loop = React\EventLoop\Factory::create();
  $socket = new React\Socket\Server('127.0.0.1:9999', $loop);
  
  $amqp_options = [
    'host' => 'localhost',
    'vhost' => 'x',
    'user' => 'x',
    'password' => 'xx',
  ];
  
  $amqp_async_client = new Bunny\Async\Client($loop, $amqp_options);
  $amqp_async_client->connect()->then(function (Bunny\Async\Client $client) use (&$socket) {
    
    $socket->on('connection', function (React\Socket\ConnectionInterface $connection) use ($client) {
      $connection->write("Hello " . $connection->getRemoteAddress() . "!\n");
      $connection->on('data', function ($data) use ($connection, $client) {
        $connection->write("You said: " . strtoupper($data) . "\n");
        $client->channel()->then(function (Bunny\Channel $channel) use ($data) {
          return $channel->exchangeDeclare('test', 'direct');
        }
        )->then(function (Bunny\Channel $channel) use ($data) {
          $channel->publish($data, [], 'test')->then( function () use ($channel) {
            $channel->close();
          });
        }
        );
      });
      $connection->on('end', function () use ($connection) {
        $connection->close();
      });
      $connection->on('error', function (Exception $e) {
        echo 'conn error: ' . $e->getMessage() . PHP_EOL;
      });
    })->on('error', function (Exception $e) {
      echo 'server error: ' . $e->getMessage() . PHP_EOL;
    })->on('end', function () {
      echo 'server quit';
    });
    
    
  });
  
  $loop->run();
@jakubkulhan
Copy link
Owner

Hi. I'd create a single channel at the server startup, declare necessary exchanges & queues and then start accepting socket connections. In multi-threaded environments, such as JVM, it's common to create channel per thread to avoid locking. PHP is single-threaded, so there don't need to be any locks, a single channel should be sufficient.

@LBanfalvi
Copy link
Author

Thanks for your reply! I tried this scenario before, it worked well, but then I read this article about concurrency handling (it's about rubybunny, but the situation is the same - singlethreaded environment "emulates" multithreading): http://rubybunny.info/articles/concurrency.html. It says "...This means that without synchronization on, publishing from multiple threads on a shared channel may result in frames being sent to RabbitMQ out of order".

@jakubkulhan
Copy link
Owner

Ruby supports threads, although as far as I know, MRI Ruby uses Global Interpreter Lock, that effectively makes it single-threaded, although there are also JRuby, IronRuby, etc. So I think RubyBunny docs refer to real concurrent access from multiple threads. This may not happen in the PHP runtime.

@LBanfalvi
Copy link
Author

Thanks Jakub, we give it a try!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants