Skip to content

Commit 9d34d5e

Browse files
authored
Support setting different idle time for each port (swoole#4290)
* Support setting different idle time for each port * optimize code * fix * Compatible * optimize code
1 parent b0146b8 commit 9d34d5e

File tree

8 files changed

+135
-41
lines changed

8 files changed

+135
-41
lines changed

examples/server/echo.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
2-
$serv = new swoole_server("0.0.0.0", 9501, SWOOLE_BASE);
2+
// $serv = new swoole_server("0.0.0.0", 9501, SWOOLE_BASE);
3+
$serv = new swoole_server("0.0.0.0", 9501);
4+
35
//$serv->on('connect', function ($serv, $fd, $reactor_id){
46
// echo "[#".posix_getpid()."]\tClient@[$fd:$reactor_id]: Connect.\n";
57
//});

ext-src/swoole_server.cc

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,19 +2385,9 @@ static PHP_METHOD(swoole_server, set) {
23852385
if (php_swoole_array_get_value(vht, "heartbeat_check_interval", ztmp)) {
23862386
zend_long v = zval_get_long(ztmp);
23872387
serv->heartbeat_check_interval = SW_MAX(0, SW_MIN(v, UINT16_MAX));
2388-
}
2389-
// heartbeat idle time
2390-
if (php_swoole_array_get_value(vht, "heartbeat_idle_time", ztmp)) {
2388+
} else if (php_swoole_array_get_value(vht, "heartbeat_idle_time", ztmp)) {
23912389
zend_long v = zval_get_long(ztmp);
2392-
serv->heartbeat_idle_time = SW_MAX(0, SW_MIN(v, UINT16_MAX));
2393-
2394-
if (serv->heartbeat_check_interval > serv->heartbeat_idle_time) {
2395-
php_swoole_fatal_error(E_WARNING, "heartbeat_idle_time must be greater than heartbeat_check_interval");
2396-
serv->heartbeat_check_interval = serv->heartbeat_idle_time / 2;
2397-
}
2398-
}
2399-
if (serv->heartbeat_idle_time == 0 && serv->heartbeat_check_interval > 0) {
2400-
serv->heartbeat_idle_time = serv->heartbeat_check_interval * 2;
2390+
serv->heartbeat_check_interval = v > 2 ? v / 2 : 1;
24012391
}
24022392
// max_request
24032393
if (php_swoole_array_get_value(vht, "max_request", ztmp)) {
@@ -3066,22 +3056,22 @@ static PHP_METHOD(swoole_server, heartbeat) {
30663056
RETURN_FALSE;
30673057
}
30683058

3069-
if (serv->heartbeat_idle_time < 1) {
3059+
if (serv->heartbeat_check_interval < 1) {
30703060
RETURN_FALSE;
30713061
}
30723062

30733063
array_init(return_value);
3074-
double checktime = microtime() - serv->heartbeat_idle_time;
3064+
double now = microtime();
30753065

3076-
serv->foreach_connection([serv, checktime, close_connection, return_value](Connection *conn) {
3077-
swTrace("heartbeat check fd=%d", conn->fd);
3078-
if (conn->protect || conn->last_recv_time == 0 || conn->last_recv_time > checktime) {
3079-
return;
3080-
}
3066+
serv->foreach_connection([serv, now, close_connection, return_value](Connection *conn) {
30813067
SessionId session_id = conn->session_id;
30823068
if (session_id <= 0) {
30833069
return;
30843070
}
3071+
swTrace("heartbeat check fd=%d", conn->fd);
3072+
if (serv->is_healthy_connection(now, conn)) {
3073+
return;
3074+
}
30853075
if (close_connection) {
30863076
conn->close_force = 1;
30873077
serv->close(session_id, false);

ext-src/swoole_server_port.cc

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,10 @@ void php_swoole_server_port_minit(int module_number) {
199199
/**
200200
* [Master-Process]
201201
*/
202-
static ssize_t php_swoole_server_length_func(Protocol *protocol, network::Socket *conn, const char *data, uint32_t length) {
202+
static ssize_t php_swoole_server_length_func(Protocol *protocol,
203+
network::Socket *conn,
204+
const char *data,
205+
uint32_t length) {
203206
Server *serv = (Server *) protocol->private_data_2;
204207
serv->lock();
205208

@@ -309,6 +312,11 @@ static PHP_METHOD(swoole_server_port, set) {
309312
port->kernel_socket_send_buffer_size = INT_MAX;
310313
}
311314
}
315+
// heartbeat idle time
316+
if (php_swoole_array_get_value(vht, "heartbeat_idle_time", ztmp)) {
317+
zend_long v = zval_get_long(ztmp);
318+
port->heartbeat_idle_time = SW_MAX(0, SW_MIN(v, UINT16_MAX));
319+
}
312320
if (php_swoole_array_get_value(vht, "buffer_high_watermark", ztmp)) {
313321
zend_long v = zval_get_long(ztmp);
314322
port->buffer_high_watermark = SW_MAX(0, SW_MIN(v, UINT32_MAX));
@@ -590,7 +598,8 @@ static PHP_METHOD(swoole_server_port, set) {
590598
delete context;
591599
RETURN_FALSE;
592600
}
593-
} ZEND_HASH_FOREACH_END();
601+
}
602+
ZEND_HASH_FOREACH_END();
594603
}
595604

596605
if (!port->ssl_context->cert_file.empty() || port->sni_contexts.empty()) {
@@ -643,15 +652,16 @@ static PHP_METHOD(swoole_server_port, on) {
643652
efree(func_name);
644653

645654
bool found = false;
646-
for (auto i = server_port_event_map.begin(); i!= server_port_event_map.end(); i++) {
655+
for (auto i = server_port_event_map.begin(); i != server_port_event_map.end(); i++) {
647656
if (!swoole_strcaseeq(name, len, i->first.c_str(), i->first.length())) {
648657
continue;
649658
}
650659

651660
found = true;
652661
int index = i->second.type;
653662
std::string property_name = std::string("on") + i->second.name;
654-
zend_update_property(swoole_server_port_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb);
663+
zend_update_property(
664+
swoole_server_port_ce, SW_Z8_OBJ_P(ZEND_THIS), property_name.c_str(), property_name.length(), cb);
655665
property->callbacks[index] =
656666
sw_zend_read_property(swoole_server_port_ce, ZEND_THIS, property_name.c_str(), property_name.length(), 0);
657667
sw_copy_to_stack(property->callbacks[index], property->_callbacks[index]);

include/swoole_server.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,8 @@ struct ListenPort {
255255
network::Socket *socket = nullptr;
256256
pthread_t thread_id = 0;
257257

258+
uint16_t heartbeat_idle_time = 0;
259+
258260
/**
259261
* check data eof
260262
*/
@@ -682,9 +684,7 @@ class Server {
682684
PipeBuffer **pipe_buffers = nullptr;
683685
double send_timeout = 0;
684686

685-
uint16_t heartbeat_idle_time = 0;
686687
uint16_t heartbeat_check_interval = 0;
687-
uint32_t heartbeat_check_lasttime = 0;
688688

689689
time_t reload_time = 0;
690690
time_t warning_time = 0;
@@ -1098,6 +1098,8 @@ class Server {
10981098
return (conn && conn->socket && conn->active && conn->socket->fd_type == SW_FD_SESSION);
10991099
}
11001100

1101+
bool is_healthy_connection(double now, Connection *conn);
1102+
11011103
static int is_dgram_event(uint8_t type) {
11021104
switch (type) {
11031105
case SW_SERVER_EVENT_RECV_DGRAM:

src/server/master.cc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,13 @@ int Server::start_check() {
315315
if (send_timeout > 0 && send_timeout < SW_TIMER_MIN_SEC) {
316316
send_timeout = SW_TIMER_MIN_SEC;
317317
}
318+
if (heartbeat_check_interval > 0) {
319+
for (auto ls : ports) {
320+
if (ls->heartbeat_idle_time == 0) {
321+
ls->heartbeat_idle_time = heartbeat_check_interval * 2;
322+
}
323+
}
324+
}
318325
for (auto ls : ports) {
319326
if (ls->protocol.package_max_length < SW_BUFFER_MIN_SIZE) {
320327
ls->protocol.package_max_length = SW_BUFFER_MIN_SIZE;
@@ -327,6 +334,12 @@ int Server::start_check() {
327334
swWarn("require onPacket callback");
328335
return SW_ERR;
329336
}
337+
if (ls->heartbeat_idle_time > 0) {
338+
int expect_heartbeat_check_interval = ls->heartbeat_idle_time > 2 ? ls->heartbeat_idle_time / 2 : 1;
339+
if (heartbeat_check_interval == 0 || heartbeat_check_interval > expect_heartbeat_check_interval) {
340+
heartbeat_check_interval = expect_heartbeat_check_interval;
341+
}
342+
}
330343
}
331344
#ifdef SW_USE_OPENSSL
332345
/**
@@ -1345,6 +1358,20 @@ void Server::check_port_type(ListenPort *ls) {
13451358
}
13461359
}
13471360

1361+
bool Server::is_healthy_connection(double now, Connection *conn) {
1362+
if (conn->protect || conn->last_recv_time == 0) {
1363+
return true;
1364+
}
1365+
auto lp = get_port_by_session_id(conn->session_id);
1366+
if (!lp) {
1367+
return true;
1368+
}
1369+
if (conn->last_recv_time > now - lp->heartbeat_idle_time) {
1370+
return true;
1371+
}
1372+
return false;
1373+
}
1374+
13481375
/**
13491376
* Return the number of ports successfully
13501377
*/

src/server/reactor_process.cc

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -437,16 +437,10 @@ static void ReactorProcess_onTimeout(Timer *timer, TimerNode *tnode) {
437437
Event notify_ev{};
438438
double now = microtime();
439439

440-
if (now < serv->heartbeat_check_lasttime + 10) {
441-
return;
442-
}
443-
444440
notify_ev.type = SW_FD_SESSION;
445441

446-
int checktime = now - serv->heartbeat_idle_time;
447-
448-
serv->foreach_connection([serv, checktime, reactor, &notify_ev](Connection *conn) {
449-
if (conn->protect || conn->last_recv_time > checktime) {
442+
serv->foreach_connection([serv, reactor, now, &notify_ev](Connection *conn) {
443+
if (serv->is_healthy_connection(now, conn)) {
450444
return;
451445
}
452446
#ifdef SW_USE_OPENSSL

src/server/reactor_thread.cc

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -780,8 +780,7 @@ int Server::start_reactor_threads() {
780780
/**
781781
* heartbeat thread
782782
*/
783-
if (heartbeat_check_interval >= 1 && heartbeat_check_interval <= heartbeat_idle_time) {
784-
swTrace("hb timer start, time: %d live time:%d", heartbeat_check_interval, heartbeat_idle_time);
783+
if (heartbeat_check_interval >= 1) {
785784
start_heartbeat_thread();
786785
}
787786

@@ -1086,15 +1085,15 @@ void Server::start_heartbeat_thread() {
10861085
SwooleTG.id = reactor_num;
10871086

10881087
while (running) {
1089-
double checktime = microtime() - heartbeat_idle_time;
1090-
foreach_connection([this, checktime](Connection *conn) {
1091-
if (conn->protect || conn->last_recv_time == 0 || conn->last_recv_time > checktime) {
1092-
return;
1093-
}
1088+
double now = microtime();
1089+
foreach_connection([this, now](Connection *conn) {
10941090
SessionId session_id = conn->session_id;
10951091
if (session_id <= 0) {
10961092
return;
10971093
}
1094+
if (is_healthy_connection(now, conn)) {
1095+
return;
1096+
}
10981097
DataHead ev{};
10991098
ev.type = SW_SERVER_EVENT_CLOSE_FORCE;
11001099
// convert fd to session_id, in order to verify the connection before the force close connection
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
--TEST--
2+
swoole_server_port: heartbeat 2
3+
--SKIPIF--
4+
<?php require __DIR__ . '/../include/skipif.inc'; ?>
5+
--FILE--
6+
<?php
7+
require __DIR__ . '/../include/bootstrap.php';
8+
9+
use function Swoole\Coroutine\go;
10+
use function Swoole\Coroutine\run;
11+
use Swoole\Coroutine\System;
12+
13+
$pm = new ProcessManager;
14+
$pm->initFreePorts(3);
15+
16+
$pm->parentFunc = function ($pid) use ($pm) {
17+
run(function () use ($pm) {
18+
$test_func = function ($port_index, $sleep_seconds) use ($pm) {
19+
$cli = new Swoole\Coroutine\Client(SWOOLE_SOCK_TCP);
20+
$cli->connect('127.0.0.1', $pm->getFreePort($port_index));
21+
System::sleep($sleep_seconds);
22+
return $cli->recv(0.01);
23+
};
24+
go(function () use ($test_func) {
25+
Assert::same($test_func(0, 1.3), '');
26+
echo "DONE 0\n";
27+
});
28+
go(function () use ($test_func) {
29+
Assert::same($test_func(1, 2.3), '');
30+
echo "DONE 1\n";
31+
});
32+
go(function () use ($test_func) {
33+
Assert::same($test_func(2, 3.3), false);
34+
echo "DONE 2\n";
35+
});
36+
});
37+
$pm->kill();
38+
};
39+
40+
$pm->childFunc = function () use ($pm)
41+
{
42+
$server = new swoole_server('127.0.0.1', $pm->getFreePort(0), SWOOLE_BASE);
43+
$server->set([
44+
'heartbeat_check_interval' => 1,
45+
'heartbeat_idle_time' => 1,
46+
]);
47+
$server->on('receive', function ($server, $fd, $reactorId, $data) {
48+
$server->send($fd, 'ok');
49+
});
50+
51+
$port2 = $server->listen('127.0.0.1', $pm->getFreePort(1), SWOOLE_SOCK_TCP);
52+
$port2->set([
53+
'heartbeat_idle_time' => 2,
54+
]);
55+
56+
$port3 = $server->listen('127.0.0.1', $pm->getFreePort(2), SWOOLE_SOCK_TCP);
57+
$port3->set([
58+
'heartbeat_idle_time' => 10,
59+
]);
60+
61+
$server->start();
62+
};
63+
64+
$pm->childFirst();
65+
$pm->run();
66+
?>
67+
--EXPECT--
68+
DONE 0
69+
DONE 1
70+
DONE 2

0 commit comments

Comments
 (0)