Lae/stubs/edge.stub

341 lines
7.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
// 节点 ID
$node_id = '{{NODE_ID}}';
// Nginx 单个站点的配置文件路径
$nginx_conf_path = '/path/to/api.laecloud.com.conf';
/** 自动生成的配置文件,如有变动,按照需求编辑。 **/
$redis_info = [
'host' => '{{REDIS_HOST}}',
'port' => '{{REDIS_PORT}}',
'password' => '{{REDIS_PASSWORD}}',
'prefix' => '{{REDIS_PREFIX}}',
];
/** 好了,不要再编辑了。配置到此为止了。 **/
$nginx_conf = '';
$nginx_file_md5 = '';
$prefix = $redis_info['prefix'];
// 检查是否存在配置文件
if (! file_exists($nginx_conf_path)) {
echo 'nginx.conf not found';
exit;
} else {
$nginx_conf = file_get_contents($nginx_conf_path);
$nginx_file_md5 = md5_file($nginx_conf_path);
}
// 检查redis_info是否正确
foreach ($redis_info as $key => $value) {
if (empty($value)) {
echo "redis_info[$key] is empty";
exit;
}
}
$redis = new Redis();
try {
$redis->connect($redis_info['host'], $redis_info['port']);
$redis->auth($redis_info['password']);
// set db 0
$redis->select(0);
} catch (RedisException $e) {
echo 'Connection to Redis failed: '.$e->getMessage();
}
try {
if ($redis->ping('')) {
output('redis is ok');
}
} catch (RedisException $e) {
output('redis is error');
}
// 检验环境
// 检测 Nginx 是否存在 which nginx
$nginx_path = trim(shell_exec('which nginx'));
if (empty($nginx_path)) {
echo 'nginx not found';
redis_publish('edge.error', [
'message' => '此节点上并没有安装 Nginx ,但是 Edge 被启动了,现在已经退出。',
]);
exit;
}
redis_publish('edge.launched');
$last_run_at = time() - 100;
$ip = '';
// 获取本机 IP ifconfig.me
$ip = trim(shell_exec('curl -s ifconfig.me'));
$run_count = 0;
// 逻辑部分
while (true) {
if ($run_count >= 10000) {
$run_count = 0;
// 重新获取 IP
$ip = trim(shell_exec('curl -s ifconfig.me'));
}
redis_hset('nodes', $node_id, [
'type' => 'edge',
'id' => 'edge-'.$node_id,
'ip' => $ip,
// utc +8 timestamp
'last_heartbeat' => time(),
]);
// 上次运行时间需要满 60s才继续否则 continue
if (time() - $last_run_at < 60) {
sleep(1);
continue;
}
// 重新载入 nginx.conf
if (! file_exists($nginx_conf_path)) {
echo 'nginx.conf not found';
redis_publish('edge.error', [
'message' => 'Nginx 配置文件在启动后被删除Edge 进程退出。',
]);
exit;
} else {
$nginx_conf = file_get_contents($nginx_conf_path);
$nginx_file_md5 = md5_file($nginx_conf_path);
}
// 检查 laecloud_database_cluster:nodes 是否存在
try {
if (! $redis->exists($prefix.'cluster:nodes')) {
output('cluster:nodes not found');
exit;
}
} catch (RedisException $e) {
output('redis is error');
exit;
}
$nodes = redis_hgetAll('nodes');
if (! $nodes) {
output('nodes is empty');
continue;
}
foreach ($nodes as $node) {
// if node last_heartbeat(utc+8 timestamp) is over 10s, skip
if (time() - $node['last_heartbeat'] > 10) {
// 删除节点
redis_hdel('nodes', $node['id']);
}
}
$conf = '';
foreach ($nodes as $node) {
// only allow node type master, slave
if (! in_array($node['type'], ['master', 'slave'])) {
continue;
}
output('node_type: '.$node['type']);
output('node_id: '.$node['id']);
output('================================================================');
$temp_conf = "#node {$node['type']}:{$node['id']}\n";
$temp_conf .= "server {$node['ip']}";
if (isset($node['weight'])) {
if ($node['weight'] !== '0') {
$temp_conf .= " weight={$node['weight']} ";
}
} else {
$temp_conf .= ' weight=1 ';
}
if ($node['type'] == 'master') {
if (count($nodes) <= 2) {
$temp_conf .= ' backup ';
}
}
$temp_conf .= ' max_fails=1 fail_timeout=10s ';
$temp_conf .= ";\n";
echo $temp_conf.PHP_EOL;
output('================================================================');
$conf .= $temp_conf.PHP_EOL;
}
output('!!!!!!!!!!!!!!!!!');
echo $conf;
output('!!!!!!!!!!!!!!!!!');
$add_header = <<<EOF
add_header 'Cluster-Ready-Node-Id' 'edge-$node_id';
add_header 'Powered-by' 'Cluster Ready!';
EOF;
// 放入配置文件,以 ##########CLUSTERREADY######### 开始,以 ##########END_CLUSTERREADY######### 结束
$nginx_conf = preg_replace('/##########CLUSTERREADY#########.*##########END_CLUSTERREADY#########/s', '##########CLUSTERREADY#########'.PHP_EOL.$conf.'##########END_CLUSTERREADY#########', $nginx_conf);
$nginx_conf = preg_replace('/##########CLUSTERREADY_PROXY#########.*##########END_CLUSTERREADY_PROXY#########/s', '##########CLUSTERREADY_PROXY#########'.PHP_EOL.$add_header.PHP_EOL.'##########END_CLUSTERREADY_PROXY#########', $nginx_conf);
if (md5($nginx_conf) != $nginx_file_md5) {
file_put_contents($nginx_conf_path, $nginx_conf);
output('nginx.conf is changed, reload nginx');
// reload nginx
exec('nginx -s reload');
output('nginx is reloaded.');
// 收尾
redis_publish('edge.deployed');
}
$last_run_at = time();
$run_count++;
}
function output($context): void
{
$time_string = date('Y-m-d H:i:s');
echo $time_string.': '.$context.PHP_EOL;
}
function redis_get($key, $default = null)
{
global $redis, $prefix;
try {
$value = $redis->get($prefix.'cluster:'.$key);
} catch (RedisException $e) {
exit('redis get error: '.$e->getMessage());
}
if (empty($value)) {
return $default;
}
return $value;
}
function redis_hget($key, $hash_key, $default = null)
{
global $redis, $prefix;
try {
$value = $redis->hget($prefix.'cluster:'.$key, $hash_key);
} catch (RedisException $e) {
exit('redis get error: '.$e->getMessage());
}
if (empty($value)) {
return $default;
}
return $value;
}
function redis_hgetAll($key)
{
global $redis, $prefix;
try {
$value = $redis->hGetAll($prefix.'cluster:'.$key);
} catch (RedisException $e) {
exit('redis get error: '.$e->getMessage());
}
// json_decode all
foreach ($value as $k => $v) {
$value[$k] = json_decode($v, true);
}
return $value;
}
function redis_hset($key, $hash_key, $value)
{
global $redis, $prefix;
try {
$redis->hset($prefix.'cluster:'.$key, $hash_key, json_encode($value));
} catch (RedisException $e) {
exit('redis get error: '.$e->getMessage());
}
}
function redis_hdel($key, $hash_key): void
{
global $redis, $prefix;
try {
$redis->hdel($prefix.'cluster:'.$key, $hash_key);
} catch (RedisException $e) {
exit('redis get error: '.$e->getMessage());
}
}
function redis_publish($event, $message = []): void
{
global $redis, $prefix, $node_id;
$data = [
'event' => $event,
'node' => [
'type' => 'edge',
'id' => $node_id,
],
'data' => $message,
];
$data = json_encode($data);
try {
$redis->publish($prefix.'cluster_ready', $data);
} catch (RedisException $e) {
exit('redis publish error: '.$e->getMessage());
}
}