Я хочу уменьшить количество открытых файлов на моем VPS. С помощью lsof я заметил, что Apache постоянно держит access.log и error.log открытыми для каждого домена, размещенного на сервере.
Есть ли способ изменить это поведение?
Многие открытые файлы обычно не являются проблемой. Существует системный предел, который вы можете проверить с помощью команды sysctl fs.file-max
- в моей системе CentOS 6 с 4 ГБ ОЗУ это 382299, что должно быть достаточно, а на виртуальной машине с 256 МБ, которую я запустил, это было 23539, что может быть несколько слишком мало. Но вы можете легко увеличить этот лимит, например, добавив к /etc/sysctl.conf
:
fs.file-max = 500000
А потом бегом sysctl -p
или перезагрузка.
Вы можете написать программу, которая будет открывать, записывать и закрывать файл журнала каждый раз, когда данные поступают из стандартного ввода, и использовать ее следующим образом:
CustomLog "||open-write-close /var/log/httpd/access_log" common
но это не будет эффективно. И это будет непросто, поскольку потребуется использовать асинхронный ввод-вывод.
Я написал для этого программу на C:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
int outfd;
int main(int argn, char* argv[])
{
if (argn != 2) {
fprintf(stderr, "Usage %s [output_file]\n", argv[0]);
return 1;
}
{
long stdinflags = fcntl(0,F_GETFL);
if ( stdinflags == -1 ) {
perror("fcntl: Can't read stdin status");
return 2;
}
if ( fcntl(0,F_SETFL,stdinflags|O_NONBLOCK) == -1 ) {
perror("fcntl: Can't set stdin to non-blocking mode");
return 2;
}
}
do {
#define BUFSIZE PIPE_BUF
char buf[BUFSIZE];
ssize_t bytesread = read(0, buf, BUFSIZE);
{
fd_set select_fdset;
FD_ZERO(&select_fdset);
FD_SET(0, &select_fdset);
if ( select(1, &select_fdset, NULL, NULL, NULL) == -1 ) {
if ( errno != EINTR ) {
perror("select");
return 2;
}
};
}
if ( bytesread==-1 ) {
perror("Can't read from stdin");
return 2;
}
if ( bytesread == 0 ) break;
outfd = open(argv[1],O_WRONLY|O_APPEND|O_CREAT);
if ( outfd < 0 ) {
fprintf(stderr, "Can't open file \"%s\": %s\n", argv[1], strerror(errno));
return 2;
}
do {
if ( write(outfd, buf, bytesread) == -1 ) {
fprintf(stderr, "Can't write to file \"%s\": %s\n", argv[1], strerror(errno));
return 2;
};
bytesread = read(0, buf, BUFSIZE);
if ( bytesread==-1 ) {
if ( errno==EAGAIN ) break;
perror("Can't read from stdin");
}
} while ( bytesread>0 );
if ( close(outfd) != 0) {
fprintf(stderr, "Can't close file \"%s\": %s\n", argv[1], strerror(errno));
return 2;
}
} while ( 1 );
return 0;
}
Если у вас действительно большое количество виртуальных хостов с отдельными файлами журнала доступа, я предлагаю записывать все в один файл (да, исключить CustomLog в каждом виртуальном хосте), а позже использовать разделенный файл журнала приложение для их разделения. Вы можете присоединиться к нему с logresolve скрипт для разрешения имен DNS.
Чтобы разделенный файл журнала работы, вам необходимо установить первый столбец в CustomLog на ServerName.