-
Hello! I wanted to use roadrunner for a vanilla php with just one task that sounds pretty much like this:
On a surface: the script works fine with PHP-FPM and completes in 150-300 ms. Server specs: Also sometimes I get this in server errors: My config: version: "3"
rpc:
listen: tcp://127.0.0.1:6001
server:
command: 'php app.php'
user: "root"
relay: pipes
status:
address: 127.0.0.1:2114
unavailable_status_code: 501
http:
address: 0.0.0.0:80
access_logs: false
internal_error_code: 505
headers:
cors:
allowed_origin: "*"
allowed_headers: "*"
pool:
supervisor:
exec_ttl: 1s
max_worker_memory: 256
ttl: 9s
watch_tick: 1s
idle_ttl: 2s
num_workers: 48
max_jobs: 150
allocate_timeout: 3s
debug: false
ssl:
cert: #...
key: #...
address: ":443"
client_auth_type: no_client_certs
http2:
h2c: false
max_concurrent_streams: 128
logs:
mode: production
level: info
output: stdout
encoding: console
file_logger_options:
log_output: "/var/www/roadrunner/logs/runner.log"
max_size: 100
max_age: 1
max_backups: 3
compress: true
channels:
http:
mode: raw
level: error
encoding: console
output: stdout
err_output: stderr
server:
mode: production
level: info
encoding: json
output: stdout
err_output: stdout
rpc:
mode: production
level: error
encoding: console
output: stdout
err_output: stdout My code: $router->get('/pop', static function (ServerRequestInterface $request): Response {
$data = $request->getQueryParams();
$db = new RemoteDB();
$guzzle = new \GuzzleHttp\Client(['timeout' => 0.05]);
try {
$user = $db->prepared_query("SELECT * FROM users WHERE `key` = ? LIMIT 1", [$data['key']])->get_result()->fetch_all(MYSQLI_ASSOC)[0];
if (!$user) {
throw new Exception("No user found!");
}
$campaign = $db->prepared_query("SELECT * FROM campaigns WHERE u_id = ? AND id = ? AND format = 2 AND status = 1 AND (`limit` > `count_request`) LIMIT 1", [$user['id'], $data['c_id']])->get_result()->fetch_all(MYSQLI_ASSOC)[0] ?? null;
if (!$campaign) {
throw new Exception("No campaign found");
}
$db->prepared_query("UPDATE campaigns SET count_request = count_request + 1 WHERE id = ?", [$data['c_id']]);
$datetime = date("Y-m-d H:i:s");
$sub_id_sql['id'] = 10101010103;
$sub_id = $sub_id_sql['sub_id'] ?? 10101010103;
$hash = $db->getRandomString(40);
$stat_id = $db->prepared_query("INSERT INTO stat_pops (c_id, hash, ua, ip, lang, referrer, sub_id, subscriber_id, subscriber_age, created_at, updated_at, country) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
[$data['c_id'], $hash, mb_substr($data['ua'], 0, 240), $data['ip'], mb_substr($data['lang'], 0, 5), $data['referrer'], $data['subid'], $data['subscriber_id'], $data['subscriber_age'], $datetime, $datetime, $data['geo'] ?? "RU"], "isssssssssss")->insert_id;
$check_excludes = $db->prepared_query("SELECT source_id FROM exclude WHERE campaign_id = ?", [$data['c_id']])->get_result()->fetch_all(MYSQLI_ASSOC) ?? null;
$exclude = [];
if ($check_excludes) {
foreach ($check_excludes as $check_exclude) {
$exclude[] = $check_exclude['source_id'];
}
}
// $sql = "SELECT * FROM source_pushes WHERE status = ? AND (qps > count_request or qps = 0) AND `format` = 2";
$sql = "SELECT * FROM source_pushes WHERE id IN (20, 33, 34, 40, 39) AND (qps > count_request or qps = 0) AND `format` = 2";
if ($campaign['type'] == 1) {
$sql .= " AND type = ?";
$array_params[] = 1;
}
if (count($exclude) >= 1) {
$sources_id = implode(',', $exclude);
$sql .= " AND id NOT IN (?)";
$array_params[] = $sources_id;
}
$sources = $db->prepared_query($sql, $array_params ?? null)->get_result()->fetch_all(MYSQLI_ASSOC);
$array_ids = [];
$array_request = [];
foreach ($sources as $source) {
$array_request[] = [
'id' => $source['id'],
'link' => $source['link'],
'log' => $source['log'],
'result' => json_decode($source['result'], true),
];
$array_ids[] = $source['id'];
}
if (isset($array_ids[0])) {
$re = implode(",", $array_ids);
$db->exec("UPDATE source_pushes SET count_request = count_request + 1 WHERE id IN ($re)");
}
$array_results = [];
$array_params = [
'ip' => $data['ip'],
'lang' => $data['lang'],
'lang_2' => substr($data['lang'], 0, 2),
'subid' => $sub_id,
'domain' => $data['referrer'],
'user_agent' => $data['ua'],
'subscriber_id' => $data['subscriber_id'],
'created_at' => $data['subscriber_age'],
'created_unix' => strtotime($data['subscriber_age'])
];
$urls = [];
$urls_res = [];
foreach ($array_request as $item) {
foreach ($array_params as $key => $array_param) {
$item['link'] = str_replace('{' . $key . '}', urlencode($array_param), $item['link']);
}
$urls_res[] = ['link' => $item['link'], 'id' => $item['id'], 'log' => $item['log']];
}
$guzzle_requests = function ($urls_res) {
foreach ($urls_res as $url)
yield new \GuzzleHttp\Psr7\Request('GET', $url['link']);
};
$guzzle_responses = [];
$pool = new \GuzzleHttp\Pool($guzzle, $guzzle_requests($urls_res), [
'concurrency' => 5,
'options' => ['timeout' => 0.1, 'connection_timeout' => 0.05],
'fulfilled' => function (GuzzleResponse $response, $index) use (&$guzzle_responses, $urls_res) {
$guzzle_responses[] = [
$response->getBody()->getContents(),
$urls_res[$index]['id'],
$urls_res[$index]['log'],
$response->getStatusCode(),
];
},
'rejected' => function (RequestException|ConnectException $reason, $index) use (&$guzzle_responses, $urls_res) {
$guzzle_responses[] = [
$reason->getMessage(),
(int) $urls_res[$index]['id'],
$urls_res[$index]['log'],
$reason->getCode(),
];
},
]);
$promise_pool = $pool->promise();
$promise_pool->wait();
if (isset($guzzle_responses)) {
foreach ($guzzle_responses as $key => $response) {
if(!isset($response[3]) || $response[3] != 200)
continue;
$id_log = $response[2];
if ($response[2] == 1) {
$array_log = [
$data['c_id'],
$response[1],
$response[3],
date('Y-m-d H:i:s'),
date('Y-m-d H:i:s'),
];
$log_id = $db->prepared_query("INSERT INTO log_sources (c_id, s_id, code, created_at, updated_at) VALUES (?,?,?,?,?)", $array_log, "iiiss")->insert_id;
}
if ($response[3] != 200 || empty($response[0]))
continue;
$id_source = $response[1];
$response = json_decode($response[0], true);
if (!$response)
continue;
// Modify response to proper result array
$result = $response['seatbid'][0]['bid'][0] ?? $response['result']['listing'][0] ?? $response['ads'][0] ?? $response['results'][0] ?? $response[0] ?? $response;
if ($id_log == 1 && isset($log_id)) {
$db->prepared_query("UPDATE log_sources SET data = ? WHERE id = ?", [json_encode($result), $log_id]);
}
if (isset($result[$array_request[$key]['result']['sum']])) {
if (isset($array_results[0])) {
if ($array_results[0]['sum'] >= $result[$array_request[$key]['result']['sum'] ?? 0]) {
continue;
}
}
$array_results[0] = [
'id' => $id_source,
'sum' => $result[$array_request[$key]['result']['sum']],
'click_url' => $result[$array_request[$key]['result']['click_url']],
];
}
}
}
if (!isset($array_results[0])) {
throw new Exception("Not found result!");
}
$data = [
round($array_results[0]['sum'] * $campaign['bid'], 6),
$array_results[0]['id'],
"https://.../?hash=$hash",
json_encode($array_results[0]),
$stat_id
];
$sql = "UPDATE stat_pops SET price = ?, partner = ?, link = ?, data = ? WHERE id = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param("dissi", ...$data);
$stmt->execute();
$result = [
'price' => round($data[0] * ($campaign['type_pay'] == 1 ? 1 : 1000), 6),
'click_url' => $data[2],
];
$db->close();
gc_collect_cycles();
return new Response(200, [], json_encode($result));
} catch (Throwable $e)
{
gc_collect_cycles();
$db->close();
return new Response(204, ['Exception' => $e->getMessage()]);
}
}); |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 39 replies
-
Hey @PakToPack 👋🏻 supervisor:
exec_ttl: 1s
max_worker_memory: 256
ttl: 9s
watch_tick: 1s
idle_ttl: 2s
|
Beta Was this translation helpful? Give feedback.
-
Gave a 1000 requests per second, 256 workers, no supervisor and no max_jobs. Can you please help with understanding on how to properly setup roadrunner for high-load task. Script elapsed time is at 0.11-0.12 sec. |
Beta Was this translation helpful? Give feedback.
-
Because you're overkilling RR allocating over 10k processes per second as I mentioned before.
Try to test just raw hello-world worker: https://github.com/roadrunner-server/rr-e2e-tests/blob/master/php_test_files/psr-worker-bench.php |
Beta Was this translation helpful? Give feedback.
-
Ok, I found an issue: PHP MySQL driver causes timeouts. Also the config tips in replies helped me to sped up the YAML configuration. Thx for answers, I'll go refactor now :) |
Beta Was this translation helpful? Give feedback.
Ok, I found an issue: PHP MySQL driver causes timeouts.
So I'm gonna try to refactor the code using PDO and without remote DB connection.
Also the config tips in replies helped me to sped up the YAML configuration.
Thx for answers, I'll go refactor now :)