Skip to content

Commit aaf3311

Browse files
authored
Merge pull request #169 from joanhey/workerman
Add Workerman
2 parents 551e2b8 + 2cee182 commit aaf3311

7 files changed

Lines changed: 244 additions & 0 deletions

File tree

frameworks/workerman/Dockerfile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
FROM ubuntu:24.04
2+
3+
ENV PROCESS_MULTIPLIER=1
4+
ENV EVENT_LOOP=Select
5+
6+
ARG DEBIAN_FRONTEND=noninteractive
7+
8+
RUN apt-get update -yqq && apt-get install -yqq software-properties-common > /dev/null
9+
RUN LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null && \
10+
apt-get update -yqq > /dev/null && apt-get upgrade -yqq > /dev/null
11+
12+
RUN apt-get install -yqq php8.5-cli php8.5-xml php8.5-zip php8.5-sqlite3 > /dev/null
13+
14+
COPY --from=composer/composer:latest-bin --link /composer /usr/local/bin/composer
15+
16+
RUN apt-get install -y php-pear php8.5-dev libevent-dev git > /dev/null && \
17+
pecl install event-3.1.4 > /dev/null && echo "extension=event.so" > /etc/php/8.5/cli/conf.d/30-event.ini
18+
19+
WORKDIR /workerman
20+
COPY --link composer.json .
21+
RUN composer install --optimize-autoloader --classmap-authoritative --no-dev --quiet
22+
COPY php.ini /etc/php/8.5/cli/php.ini
23+
24+
COPY --link . .
25+
26+
EXPOSE 8080
27+
28+
CMD ["php", "/workerman/server.php", "start"]

frameworks/workerman/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Workerman
2+
3+
An asynchronous event driven PHP socket framework. Supports HTTP, Websocket, SSL and other custom protocols.
4+
5+
All in pure PHP code.
6+
7+
https://github.com/walkor/workerman
8+
https://manual.workerman.net/doc/en/
9+
https://www.workerman.net/
10+
11+
12+
## Stack
13+
14+
- **Language:** PHP
15+
- **Engine:** Workerman (event)
16+
17+
18+
## Endpoints
19+
20+
| Endpoint | Method | Description |
21+
|----------|--------|-------------|
22+
| `/pipeline` | GET | Returns `ok` (plain text) |
23+
| `/baseline11` | GET | Sums query parameter values |
24+
| `/baseline11` | POST | Sums query parameters + request body |
25+
| `/json` | GET | Processes 50-item dataset, serializes JSON |
26+
| `/compression` | GET | Gzip-compressed large JSON response |
27+
| `/db` | GET | SQLite range query with JSON response |
28+
| `/upload` | POST | Receives 1 MB body, returns byte count |
29+
| `/static/{filename}` | GET | Serves preloaded static files |
30+
31+
## Notes
32+
33+
- No chunked requests for now (WIP)
34+
- Chunked responses OK
35+
- Per-worker database connection
36+

frameworks/workerman/composer.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"require": {
3+
"workerman/workerman": "^5"
4+
},
5+
"autoload": {
6+
"psr-4": {
7+
"": "./"
8+
}
9+
}
10+
}

frameworks/workerman/db.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
class DB
4+
{
5+
public static $db;
6+
public static $prepared;
7+
8+
public static function init()
9+
{
10+
self::$db = new Sqlite3('/data/benchmark.db', SQLITE3_OPEN_READONLY);
11+
12+
self::$prepared = self::$db->prepare('SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count
13+
FROM items
14+
WHERE price BETWEEN ? AND ?
15+
LIMIT 50');
16+
}
17+
18+
public static function query($min, $max)
19+
{
20+
self::$prepared->bindValue(1, $min, SQLITE3_FLOAT);
21+
self::$prepared->bindValue(2, $max, SQLITE3_FLOAT);
22+
23+
$result = self::$prepared->execute();
24+
25+
$data = [];
26+
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
27+
$data[] = [
28+
'id' => $row['id'],
29+
'name' => $row['name'],
30+
'category' => $row['category'],
31+
'price' => $row['price'],
32+
'quantity' => $row['quantity'],
33+
'active' => (bool) $row["active"],
34+
'tags' => json_decode($row["tags"], true),
35+
'rating' => [
36+
"score" => $row["rating_score"],
37+
"count" => $row["rating_count"]],
38+
];
39+
}
40+
return json_encode(['items' => $data, 'count' => count($data)]);
41+
}
42+
}

frameworks/workerman/meta.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"display_name": "workerman",
3+
"language": "PHP",
4+
"type": "framework",
5+
"engine": "workerman",
6+
"description": "Workerman with PHP content handlers for all benchmark endpoints.",
7+
"repo": "https://github.com/walkor/Workerman",
8+
"enabled": true,
9+
"tests": [
10+
"pipelined",
11+
"baseline",
12+
"noisy",
13+
"json",
14+
"compression",
15+
"limited-conn",
16+
"mixed"
17+
]
18+
}

frameworks/workerman/php.ini

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
opcache.enable=1
2+
opcache.enable_cli=1
3+
opcache.validate_timestamps=0
4+
opcache.save_comments=0
5+
opcache.enable_file_override=1
6+
opcache.huge_code_pages=1
7+
8+
memory_limit = 512M
9+
opcache.jit_buffer_size=128M
10+
opcache.jit=tracing

frameworks/workerman/server.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
use Workerman\Worker;
4+
use Workerman\Protocols\Http\Response;
5+
6+
require_once __DIR__ . '/vendor/autoload.php';
7+
require_once __DIR__ . '/db.php';
8+
9+
// #### http worker ####
10+
$http_worker = new Worker('http://0.0.0.0:8080');
11+
$http_worker->reusePort = true;
12+
13+
// 1 process per CPU core
14+
$http_worker->count = (int) shell_exec('nproc');
15+
16+
// benchmark data
17+
define('JSON_DATA', json_decode(file_get_contents('/data/dataset.json'), true));
18+
define('LARGE_JSON', largeJson());
19+
20+
function largeJson()
21+
{
22+
$data = json_decode(file_get_contents('/data/dataset-large.json'), true);
23+
foreach ($data as &$item) {
24+
$item['total'] = $item['price'] * $item['quantity'];
25+
}
26+
27+
return json_encode(['items' => $data, 'count' => count($data)]);
28+
}
29+
30+
$http_worker->onWorkerStart = static function () {
31+
DB::Init();
32+
};
33+
34+
// Data received
35+
$http_worker->onMessage = static function ($connection, $request) {
36+
switch ($request->path()) {
37+
case '/pipeline':
38+
$connection->headers = ['Content-Type' => 'text/plain'];
39+
return $connection->send('ok');
40+
41+
case '/baseline11':
42+
$sum = array_sum($request->get());
43+
if($request->method() === 'POST') {
44+
$sum += $request->rawBody();
45+
}
46+
47+
$connection->headers = ['Content-Type' => 'text/plain'];
48+
return $connection->send($sum);
49+
50+
case '/json':
51+
$total = [];
52+
foreach (JSON_DATA as $item) {
53+
$item['total'] = $item['price'] * $item['quantity'];
54+
$total[] = $item;
55+
}
56+
57+
$connection->headers = ['Content-Type' => 'application/json'];
58+
return $connection->send(json_encode(['items' => $total, 'count' => count($total)]));
59+
60+
// case '/upload':
61+
// $connection->headers = ['Content-Type' => 'text/plain'];
62+
// return $connection->send(strlen($request->rawBody()));
63+
64+
case '/compression':
65+
if (str_contains($request->header('Accept-Encoding', ''), 'gzip')) {
66+
$connection->headers = [
67+
'Content-Type' => 'application/json',
68+
'Content-Encoding' => 'gzip'
69+
];
70+
return $connection->send(gzencode(LARGE_JSON, 1));
71+
}
72+
73+
$resp = new Response(200, ['Content-Type' => 'application/json'], LARGE_JSON);
74+
return $connection->send($resp);
75+
76+
case '/db':
77+
$connection->headers = ['Content-Type' => 'application/json'];
78+
return $connection->send(
79+
DB::query(
80+
$request->get('min', 10),
81+
$request->get('max', 50)
82+
)
83+
);
84+
}
85+
86+
// Serve static files
87+
// if (str_starts_with($request->path(), '/static/')) {
88+
// $response = (new Response())->withFile('/data' . $request->path());
89+
// return $connection->send($response);
90+
// }
91+
92+
return $connection->send(new Response(
93+
404,
94+
['Content-Type' => 'text/plain'],
95+
'404 Not Found')
96+
);
97+
};
98+
99+
// Run all workers
100+
Worker::runAll();

0 commit comments

Comments
 (0)