1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/swoole-swoole

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Swoole — это высокопроизводительная библиотека для PHP, основанная на событиях, асинхронности и сопрограммах.

⚙️ Быстрый старт

Запустите программу Swoole с помощью Docker:

docker run --rm phpswoole/swoole "php --ri swoole"

Подробнее см.: How to Use This Image.

Документация https://wiki.swoole.com/

HTTP-сервис

$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++);
        }
    });
});

Runtime Hook

Swoole подключает блокирующие функции ввода-вывода PHP на нижнем уровне и автоматически преобразует их в неблокирующие функции, чтобы эти функции можно было вызывать одновременно в сопрограммах.

Поддерживаемые расширения/функции

Awesome Swoole

Проект Awesome Swoole (https://github.com/swoole/awesome-swoole) содержит список замечательных вещей, связанных с Swoole, включая:

  • Фреймворки и библиотеки на основе Swoole;
  • Пакеты для интеграции Swoole с популярными PHP-фреймворками, такими как Laravel, Symfony, Slim и Yii;
  • Книги, видео и другие учебные материалы о Swoole;
  • Инструменты для отладки, профилирования и тестирования приложений на базе 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!  
});  

Timer

$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";
    }
});

The way of coroutine

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";
});

🔥 Amazing runtime hooks

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!

How many things you can do in 1s?

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 всегда обеспечивает максимальную надёжность и самые мощные функции в последней выпущенной версии. Пожалуйста, убедитесь, насколько это возможно, что вы используете последнюю версию.

Требования к компиляции

  • Linux, OS X или Cygwin, WSL
  • PHP 8.1.0 или более поздняя версия (чем выше версия, тем лучше производительность.)
  • GCC 4.8 или более поздняя версия

1. Установка через PECL (для начинающих)

pecl install swoole

2. Установка из исходного кода (рекомендуется)

Скачайте исходные пакеты с Releases или:

git clone https://github.com/swoole/swoole-src.git && \
cd swoole-src

Скомпилируйте и установите в исходной папке:

phpize && \
./configure && \
make && make install

Включение расширения в PHP

После успешной компиляции и установки в систему необходимо добавить новую строку 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

  1. pecl upgrade swoole
  2. cd swoole-src && git pull && make clean && make && sudo make install
  3. Если вы меняете версию PHP, пожалуйста, повторно запустите phpize clean && phpize, затем попробуйте скомпилировать.

Основные изменения после версии 4.3.0

Асинхронные клиенты и 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.

🍭 Бенчмарк

  • На открытых бенчмарках Techempower Web Framework Swoole использовал MySQL database benchmark для того, чтобы занять первое место, и все тесты производительности были ранжированы в первом эшелоне.
  • Вы можете просто запустить Benchmark Script, чтобы быстро протестировать максимальный QPS Swoole-HTTP-Server на вашем компьютере.

🔰️ Проблемы безопасности

О проблемах безопасности следует сообщать конфиденциально по электронной почте команде разработчиков 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 )

Вы можете оставить комментарий после Вход в систему

Введение

Библиотека параллельного выполнения на основе корутин для PHP. Развернуть Свернуть
Apache-2.0
Отмена

Обновления (2)

все

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/swoole-swoole.git
git@api.gitlife.ru:oschina-mirror/swoole-swoole.git
oschina-mirror
swoole-swoole
swoole-swoole
master