Swoole — это высокопроизводительная библиотека для PHP, основанная на событиях, асинхронности и сопрограммах.
⚙️ Быстрый старт
Запустите программу Swoole с помощью Docker:
docker run --rm phpswoole/swoole "php --ri swoole"
Подробнее см.: How to Use This Image.
Документация https://wiki.swoole.com/
$http = new Swoole\Http\Server('127.0.0.1', 9501);
$http->set(['hook_flags' => SWOOLE_HOOK_ALL]);
$http->on('request', function ($request, $response) {
$result = [];
Co::join([
go(function () use (&$result) {
$result['google'] = file_get_contents("https://www.google.com/");
}),
go(function () use (&$result) {
$result['taobao'] = file_get_contents("https://www.taobao.com/");
})
]);
$response->end(json_encode($result));
});
$http->start();
Co\run(function() {
Co\go(function() {
while(1) {
sleep(1);
$fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30);
echo fread($fp, 8192), PHP_EOL;
}
});
Co\go(function() {
$fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
while(1) {
$conn = stream_socket_accept($fp);
fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));
}
});
Co\go(function() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
while(true) {
$redis->subscribe(['test'], function ($instance, $channelName, $message) {
echo 'New redis message: '.$channelName, "==>", $message, PHP_EOL;
});
}
});
Co\go(function() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$count = 0;
while(true) {
sleep(2);
$redis->publish('test','hello, world, count='.$count++);
}
});
});
Swoole подключает блокирующие функции ввода-вывода PHP на нижнем уровне и автоматически преобразует их в неблокирующие функции, чтобы эти функции можно было вызывать одновременно в сопрограммах.
ext-curl
(поддержка symfony
и guzzle
)
ext-redis
ext-mysqli
ext-pdo_mysql
ext-pdo_pgsql
ext-pdo_sqlite
ext-pdo_oracle
ext-pdo_odbc
потоковые функции (например, stream_socket_client
/stream_socket_server
) Разработка и обсуждение
IDE Helper & API: https://github.com/swoole/ide-helper
Twitter: https://twitter.com/phpswoole
Discord: https://discord.swoole.dev
Китайское сообщество: https://wiki.swoole.com/#/other/discussion
Awesome Swoole
Проект Awesome Swoole (https://github.com/swoole/awesome-swoole) содержит список замечательных вещей, связанных с Swoole, включая:
Основанный на событиях
Сетевой уровень в Swoole основан на событиях и полностью использует возможности реализации epoll/kqueue, что делает его действительно простым в обслуживании миллионов запросов.
Swoole 4.x использует новое ядро движка и теперь имеет постоянную команду разработчиков, поэтому мы вступаем в беспрецедентный период в истории PHP, который предлагает уникальную возможность для быстрого развития производительности.
Корутина
Swoole 4.x или более поздняя версия поддерживает встроенную корутину с высокой доступностью, и вы можете использовать полностью синхронизированный код для реализации асинхронной производительности без каких-либо дополнительных ключевых слов.
Разработчики могут понимать корутины как ультралёгкие потоки, и вы легко можете создать тысячи корутин в одном процессе.
MySQL
10 тысяч одновременных запросов на чтение данных из MySQL занимают всего 0,2 секунды!
$s = microtime(true);
Co\run(function() {
for ($c = 100; $c--;) {
go(function () {
$mysql = new Swoole\Coroutine\MySQL;
$mysql->connect([
'host' => '127.0.0.1',
'user' => 'root',
'password' => 'root',
'database' => 'test'
]);
$statement = $mysql->prepare('SELECT * FROM `user`');
for ($n = 100; $n--;) {
$result = $statement->execute();
assert(count($result) > 0);
}
});
}
});
echo 'use ' . (microtime(true) - $s) . ' s';
Смешанный сервер
Вы можете создавать несколько сервисов на одном цикле событий: TCP, HTTP, Websocket и HTTP2, и легко обрабатывать тысячи запросов.
function tcp_pack(string $data): string
{
return pack('N', strlen($data)) . $data;
}
function tcp_unpack(string $data): string
{
return substr($data, 4, unpack('N', substr($data, 0, 4))[1]);
}
$tcp_options = [
'open_length_check' => true,
'package_length_type' => 'N',
'package_length_offset' => 0,
'package_body_offset' => 4
];
$server = new Swoole\WebSocket\Server('127.0.0.1', 9501, SWOOLE_BASE);
$server->set(['open_http2_protocol' => true]);
// http && http2
$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) {
$response->end('Hello ' . $request->rawcontent());
});
// websocket
$server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) {
$server->push($frame->fd, 'Hello ' . $frame->data);
});
// tcp
$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP);
$tcp_server->set($tcp_options);
$tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) {
$server->send($fd, tcp_pack('Hello ' . tcp_unpack($data)));
});
$server->start();
Клиенты корутин
Независимо от того, выполняете ли вы DNS-запрос, отправляете запросы или получаете ответы, всё это автоматически планируется корутиной.
go(function () {
// http
$http_client = new Swoole\Coroutine\Http\Client('127.0.0.1', 9501);
assert($http_client->post('/', 'Swoole Http'));
``` ### Channel
Channel — это единственный способ обмена данными между корутинами. Комбинация разработки Coroutine + Channel представляет собой известную модель программирования CSP.
В разработке Swoole Channel обычно используется для реализации пула соединений или параллельного планирования корутин.
#### Пример пула соединений
В следующем примере мы имеем тысячу одновременных запросов к Redis. Обычно это превышает максимальное количество настроек подключений Redis и вызовет исключение подключения, но пул подключений на основе Channel может идеально планировать запросы. Нам не нужно беспокоиться о перегрузке соединения.
```php
class RedisPool {
/**@var \Swoole\Coroutine\Channel */
protected $pool;
/**
* RedisPool constructor.
* @param int $size max connections
*/
public function __construct(int $size = 100) {
$this->pool = new \Swoole\Coroutine\Channel($size);
for ($i = 0; $i < $size; $i++) {
$redis = new \Swoole\Coroutine\Redis();
$res = $redis->connect('127.0.0.1', 6379);
if ($res == false) {
throw new \RuntimeException("failed to connect redis server.");
} else {
$this->put($redis);
}
}
}
public function get(): \Swoole\Coroutine\Redis {
return $this->pool->pop();
}
public function put(\Swoole\Coroutine\Redis $redis) {
$this->pool->push($redis);
}
public function close(): void {
$this->pool->close();
$this->pool = null;
}
}
go(function () {
$pool = new RedisPool();
// max concurrency num is more than max connections
// but it's no problem, channel will help you with scheduling
for ($c = 0; $c < 1000; $c++) {
go(function () use ($pool, $c) {
for ($n = 0; $n < 100; $n++) {
$redis = $pool->get();
assert($redis->set("awesome-{$c}-{$n}", 'swoole'));
assert($redis->get("awesome-{$c}-{$n}") === 'swoole');
assert($redis->delete("awesome-{$c}-{$n}"));
$pool->put($redis);
}
});
}
});
Некоторые клиенты Swoole реализуют режим отсрочки для параллелизма, но вы всё равно можете гибко реализовать его с помощью комбинации корутин и каналов.
go(function () {
// User: I need you to bring me some information back.
// Channel: OK! I will be responsible for scheduling.
$channel = new Swoole\Coroutine\Channel;
go(function () use ($channel) {
// Coroutine A: Ok! I will show you the github addr info
$addr_info = Co::getaddrinfo('github.com');
$channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]);
});
go(function () use ($channel) {
// Coroutine B: Ok! I will show you what your code look like
$mirror = Co::readFile(__FILE__);
$channel->push(['B', $mirror]);
});
go(function () use ($channel) {
// Coroutine C: Ok! I will show you the date
$channel->push(['C', date(DATE_W3C)]);
};
for ($i = 3; $i--;) {
list($id, $data) = $channel->pop();
echo "From {$id}:\n
Обратите внимание, что в тексте запроса присутствуют фрагменты кода на языке PHP. В переводе они были сохранены без изменений. {$data}\n";
}
// User: Amazing, I got every information at earliest time!
});
$id = Swoole\Timer::tick(100, function () {
echo "⚙️ Do something...\n";
});
Swoole\Timer::after(500, function () use ($id) {
Swoole\Timer::clear($id);
echo "⏰ Done\n";
});
Swoole\Timer::after(1000, function () use ($id) {
if (!Swoole\Timer::exists($id)) {
echo "✅ All right!\n";
}
});
go(function () {
$i = 0;
while (true) {
Co::sleep(0.1);
echo "📝 Do something...\n";
if (++$i === 5) {
echo "🛎 Done\n";
break;
}
}
echo "🎉 All right!\n";
});
As of Swoole v4.1.0, we added the ability to transform synchronous PHP network libraries into co-routine libraries using a single line of code.
Simply call the Swoole\Runtime::enableCoroutine()
method at the top of your script. In the sample below we connect to php-redis and concurrently read 10k requests in 0.1s:
Swoole\Runtime::enableCoroutine();
$s = microtime(true);
Co\run(function() {
for ($c = 100; $c--;) {
go(function () {
($redis = new Redis)->connect('127.0.0.1', 6379);
for ($n = 100; $n--;) {
assert($redis->get('awesome') === 'swoole');
}
});
}
});
echo 'use ' . (microtime(true) - $s) . ' s';
By calling this method, the Swoole kernel replaces ZendVM stream function pointers. If you use php_stream
based extensions, all socket operations can be dynamically converted to be asynchronous IO scheduled by coroutine at runtime!
Sleep 10K times, read, write, check and delete files 10K times, use PDO and MySQLi to communicate with the database 10K times, create a TCP server and multiple clients to communicate with each other 10K times, create a UDP server and multiple clients to communicate with each other 10K times... Everything works well in one process!
Just see what the Swoole brings, just imagine...
Swoole\Runtime::enableCoroutine();
$s = microtime(true);
Co\run(function() {
// i just want to sleep...
for ($c = 100; $c--;) {
go(function () {
for ($n = 100; $n--;) {
usleep(1000);
}
});
}
// 10K file read and write
for ($c = 100; $c--;) {
go(function () use ($c) {
$tmp_filename = "/tmp/test-{$c}.php";
for ($n = 100; $n--;) {
$self = file_get_contents(__FILE__);
file_put_contents($tmp_filename, $self);
assert(file_get_contents($tmp_filename) === $self);
}
unlink($tmp_filename);
});
}
// 10K pdo and mysqli read
for ($c = 50; $c--;) {
go(function () {
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root');
$statement = $pdo->prepare('SELECT * FROM `user`');
for ($n = 100; $n--;) {
$statement->execute();
assert(count($statement->fetchAll()) > 0);
}
});
}
for ($c = 50; $c--;) {
go(function () {
$mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');
$statement = $mysqli->prepare('SELECT `id` FROM `user`');
for ($n = 100; $n--;) {
$statement->bind_result($id);
$statement->execute();
$statement->fetch();
assert($id > 0);
}
});
}
// php_stream tcp server & client with 12.8K requests in single process
function tcp_pack(string $data): string
{
return pack('n', strlen($data)) . $data;
}
function tcp_length(string $head): int
{
return unpack('n', $head)[1];
}
go(function () {
$ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]);
$socket = stream_socket_server(
'tcp://0.0.0.0:9502',
$errno,
В запросе представлен текст на языке PHP, который описывает использование библиотеки Swoole для создания асинхронных приложений. В тексте используются функции и конструкции языка PHP, а также методы и свойства библиотеки Swoole.
Текст содержит примеры кода, которые демонстрируют использование различных возможностей библиотеки Swoole, таких как таймеры, корутины и асинхронные операции ввода-вывода. ## ⌛️ Установка
Как и в любом проекте с открытым исходным кодом, Swoole всегда обеспечивает максимальную надёжность и самые мощные функции в последней выпущенной версии. Пожалуйста, убедитесь, насколько это возможно, что вы используете последнюю версию.
pecl install swoole
Скачайте исходные пакеты с Releases или:
git clone https://github.com/swoole/swoole-src.git && \
cd swoole-src
Скомпилируйте и установите в исходной папке:
phpize && \
./configure && \
make && make install
После успешной компиляции и установки в систему необходимо добавить новую строку extension=swoole.so
в php.ini
, чтобы включить расширение Swoole.
например:
./configure --enable-openssl --enable-sockets
--enable-openssl
или --with-openssl-dir=DIR
--enable-sockets
--enable-mysqlnd
(требуется mysqlnd, он просто для поддержки метода $mysql->escape
)--enable-swoole-curl
⚠️ При обновлении из исходного кода не забудьте выполнить команду
make clean
Перед обновлением swoole
pecl upgrade swoole
cd swoole-src && git pull && make clean && make && sudo make install
phpize clean && phpize
, затем попробуйте скомпилировать.Асинхронные клиенты и API перемещены в отдельное PHP-расширение swoole_async
начиная с версии 4.3.0. Установите swoole_async
:
git clone https://github.com/swoole/ext-async.git
cd ext-async
phpize
./configure
make -j 4
sudo make install
Включите его, добавив новую строку extension=swoole_async.so
в php.ini
.
О проблемах безопасности следует сообщать конфиденциально по электронной почте команде разработчиков Swoole [team@swoole.com]. Вы должны получить ответ в течение 24 часов. Если по какой-то причине вы не получили ответа, пожалуйста, отправьте повторное сообщение по электронной почте, чтобы убедиться, что мы получили ваше первоначальное сообщение.
Ваш вклад в развитие Swoole очень приветствуется!
Вы можете внести свой вклад следующими способами:
Этот проект существует благодаря всем людям, которые вносят свой вклад. [Участники].
Демин работает с PHP с 2000 года, фокусируясь на создании высокопроизводительных и безопасных веб-сервисов. Он иногда выступает на конференциях по PHP и Swoole и уже много лет работает в таких компаниях, как eBay, Visa и Glu Mobile. Вы можете найти Демина в Twitter или GitHub.
Apache License Version 2.0 см. http://www.apache.org/licenses/LICENSE-2.0.html
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )