Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #418 from limingxinleo/ws-server
Allows send WebSocket message to any fd in current server, even the worker process does not hold the fd
- Loading branch information
Showing
10 changed files
with
281 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
/** | ||
* This file is part of Hyperf. | ||
* | ||
* @link https://www.hyperf.io | ||
* @document https://doc.hyperf.io | ||
* @contact group@hyperf.io | ||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE | ||
*/ | ||
|
||
namespace Hyperf\Server; | ||
|
||
use Psr\Container\ContainerInterface; | ||
|
||
class SwooleServerFactory | ||
{ | ||
public function __invoke(ContainerInterface $container) | ||
{ | ||
$factory = $container->get(ServerFactory::class); | ||
|
||
return $factory->getServer()->getServer(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
src/websocket-server/src/Exception/InvalidMethodException.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
/** | ||
* This file is part of Hyperf. | ||
* | ||
* @link https://www.hyperf.io | ||
* @document https://doc.hyperf.io | ||
* @contact group@hyperf.io | ||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE | ||
*/ | ||
|
||
namespace Hyperf\WebSocketServer\Exception; | ||
|
||
use Hyperf\Server\Exception\ServerException; | ||
|
||
class InvalidMethodException extends ServerException | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
src/websocket-server/src/Listener/OnPipeMessageListener.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
/** | ||
* This file is part of Hyperf. | ||
* | ||
* @link https://www.hyperf.io | ||
* @document https://doc.hyperf.io | ||
* @contact group@hyperf.io | ||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE | ||
*/ | ||
|
||
namespace Hyperf\WebSocketServer\Listener; | ||
|
||
use Hyperf\Contract\StdoutLoggerInterface; | ||
use Hyperf\Event\Contract\ListenerInterface; | ||
use Hyperf\ExceptionHandler\Formatter\FormatterInterface; | ||
use Hyperf\Framework\Event\OnPipeMessage; | ||
use Hyperf\WebSocketServer\Sender; | ||
use Hyperf\WebSocketServer\SenderPipeMessage; | ||
use Psr\Container\ContainerInterface; | ||
|
||
class OnPipeMessageListener implements ListenerInterface | ||
{ | ||
/** | ||
* @var ContainerInterface | ||
*/ | ||
private $container; | ||
|
||
/** | ||
* @var StdoutLoggerInterface | ||
*/ | ||
private $logger; | ||
|
||
/** | ||
* @var Sender | ||
*/ | ||
private $sender; | ||
|
||
public function __construct(ContainerInterface $container, StdoutLoggerInterface $logger, Sender $sender) | ||
{ | ||
$this->container = $container; | ||
$this->logger = $logger; | ||
$this->sender = $sender; | ||
} | ||
|
||
/** | ||
* @return string[] returns the events that you want to listen | ||
*/ | ||
public function listen(): array | ||
{ | ||
return [ | ||
OnPipeMessage::class, | ||
]; | ||
} | ||
|
||
/** | ||
* Handle the Event when the event is triggered, all listeners will | ||
* complete before the event is returned to the EventDispatcher. | ||
*/ | ||
public function process(object $event) | ||
{ | ||
if ($event instanceof OnPipeMessage && $event->data instanceof SenderPipeMessage) { | ||
/** @var SenderPipeMessage $message */ | ||
$message = $event->data; | ||
|
||
try { | ||
$this->sender->proxy($message->name, $message->arguments); | ||
} catch (\Throwable $exception) { | ||
$formatter = $this->container->get(FormatterInterface::class); | ||
$this->logger->warning($formatter->format($exception)); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
/** | ||
* This file is part of Hyperf. | ||
* | ||
* @link https://www.hyperf.io | ||
* @document https://doc.hyperf.io | ||
* @contact group@hyperf.io | ||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE | ||
*/ | ||
|
||
namespace Hyperf\WebSocketServer; | ||
|
||
use Hyperf\Contract\StdoutLoggerInterface; | ||
use Hyperf\WebSocketServer\Exception\InvalidMethodException; | ||
use Psr\Container\ContainerInterface; | ||
use Swoole\Server; | ||
|
||
/** | ||
* @method push(int $fd, $data, int $opcode = null, $finish = null) | ||
*/ | ||
class Sender | ||
{ | ||
/** | ||
* @var ContainerInterface | ||
*/ | ||
protected $container; | ||
|
||
/** | ||
* @var StdoutLoggerInterface | ||
*/ | ||
protected $logger; | ||
|
||
/** | ||
* @var int | ||
*/ | ||
protected $workerId; | ||
|
||
public function __construct(ContainerInterface $container) | ||
{ | ||
$this->container = $container; | ||
$this->logger = $container->get(StdoutLoggerInterface::class); | ||
} | ||
|
||
public function __call($name, $arguments) | ||
{ | ||
if (! $this->proxy($name, $arguments)) { | ||
$this->sendPipeMessage($name, $arguments); | ||
} | ||
} | ||
|
||
public function proxy(string $name, array $arguments): bool | ||
{ | ||
$fd = $this->getFdFromProxyMethod($name, $arguments); | ||
|
||
$result = $this->check($fd); | ||
if ($result) { | ||
$this->getServer()->push(...$arguments); | ||
$this->logger->debug("[WebSocket] Worker.{$this->workerId} send to #{$fd}"); | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
public function setWorkerId(int $workerId): void | ||
{ | ||
$this->workerId = $workerId; | ||
} | ||
|
||
public function check($fd): bool | ||
{ | ||
$info = $this->getServer()->connection_info($fd); | ||
|
||
if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_ACTIVE) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
protected function getFdFromProxyMethod(string $method, array $arguments): int | ||
{ | ||
if (! in_array($method, ['push', 'send', 'sendto'])) { | ||
throw new InvalidMethodException(sprintf('Method [%s] is not allowed.', $method)); | ||
} | ||
|
||
return (int) $arguments[0]; | ||
} | ||
|
||
protected function getServer(): Server | ||
{ | ||
return $this->container->get(Server::class); | ||
} | ||
|
||
protected function sendPipeMessage(string $name, array $arguments): void | ||
{ | ||
$server = $this->getServer(); | ||
$workerCount = $server->setting['worker_num'] - 1; | ||
for ($workerId = 0; $workerId <= $workerCount; ++$workerId) { | ||
if ($workerId !== $this->workerId) { | ||
$server->sendMessage(new SenderPipeMessage($name, $arguments), $workerId); | ||
$this->logger->debug("[WebSocket] Let Worker.{$workerId} try to {$name}."); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
/** | ||
* This file is part of Hyperf. | ||
* | ||
* @link https://www.hyperf.io | ||
* @document https://doc.hyperf.io | ||
* @contact group@hyperf.io | ||
* @license https://github.com/hyperf-cloud/hyperf/blob/master/LICENSE | ||
*/ | ||
|
||
namespace Hyperf\WebSocketServer; | ||
|
||
class SenderPipeMessage | ||
{ | ||
/** | ||
* @var string | ||
*/ | ||
public $name; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
public $arguments; | ||
|
||
public function __construct(string $name, array $arguments) | ||
{ | ||
$this->name = $name; | ||
$this->arguments = $arguments; | ||
} | ||
} |
Oops, something went wrong.