qmail의 원본 C 소스를 직접 수정하여 파일 시스템 I/O를 발생시키는 루틴을 제거하고 KeyDB(Redis) 클라이언트 라이브러리를 통해 메모리에 직접 데이터를 적재하도록 아키텍처를 변경하는 것이 가장 근본적이고 성능면에서 확실한 방법입니다. 이 방식을 적용하려면 C언어용 Redis 클라이언트인 hiredis 라이브러리가 필요합니다. qmail-queue의 역할을 100% 대체하며 파일 시스템 접근 없이 fd 0(메시지)과 fd 1(인벨로프)을 읽어 KeyDB로 바로 밀어넣는 완전한 대체 C 소스 코드를 제공합니다. 1. 사전 준비 (hiredis 라이브러리 설치) 운영체제에 맞게 hiredis 라이브러리를 설치하십시오. Arch Linux: Bash pacman -S hiredis FreeBSD: Bash pkg install hiredis 2. qmail-queue 대체 C 소스 코드 ( qmail-queue-keydb.c ) qmail 소스 디렉토리 내에 아래 코드를 qmail-queue-keydb.c 라는 이름으로 저장하십시오. 이 코드는 이전 워커 스크립트와 호환되도록 데이터를 헥스 인코딩하여 JSON 형태로 KeyDB의 qmail_keydb_queue 리스트에 저장합니다. 누락된 부분 없는 전체 소스입니다. C #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/time.h> #include <hiredis/hiredis.h> #define CHUNK_SIZE 8192 #define EXIT_TEMP_FAIL 111 #define EXIT_SUCCESS 0 ssize_t read_fd(int fd, unsigned char **buffer, size_t *out_len) { size_t capacity = CHUNK_SIZE; size_t length = 0; ssize_t bytes_read; *buffer = malloc(capacity); if (*buffer == NULL) { return -1; } while (1) { if (length + CHUNK_SIZE > capacity) { capacity *= 2; unsigned char *new_buffer = realloc(*buffer, capacity); if (new_buffer == NULL) { free(*buffer); return -1; } *buffer = new_buffer; } bytes_read = read(fd, (*buffer) + length, CHUNK_SIZE); if (bytes_read < 0) { free(*buffer); return -1; } if (bytes_read == 0) { break; } length += bytes_read; } *out_len = length; return length; } char *hex_encode(const unsigned char *data, size_t len) { if (len == 0) { char *empty = malloc(1); empty[0] = '\0'; return empty; } char *hex = malloc((len * 2) + 1); if (hex == NULL) { return NULL; } for (size_t i = 0; i < len; i++) { sprintf(hex + (i * 2), "%02x", data[i]); } hex[len * 2] = '\0'; return hex; } int main(int argc, char **argv) { unsigned char *msg_data = NULL; unsigned char *env_data = NULL; size_t msg_len = 0; size_t env_len = 0; if (read_fd(0, &msg_data, &msg_len) < 0) { return EXIT_TEMP_FAIL; } if (read_fd(1, &env_data, &env_len) < 0) { free(msg_data); return EXIT_TEMP_FAIL; } char *msg_hex = hex_encode(msg_data, msg_len); char *env_hex = hex_encode(env_data, env_len); free(msg_data); free(env_data); if (msg_hex == NULL || env_hex == NULL) { if (msg_hex) free(msg_hex); if (env_hex) free(env_hex); return EXIT_TEMP_FAIL; } struct timeval tv; gettimeofday(&tv, NULL); char job_id[64]; snprintf(job_id, sizeof(job_id), "%ld-%d", (long)tv.tv_sec, getpid()); size_t json_len = strlen(job_id) + strlen(msg_hex) + strlen(env_hex) + 128; char *json_payload = malloc(json_len); if (json_payload == NULL) { free(msg_hex); free(env_hex); return EXIT_TEMP_FAIL; } snprintf(json_payload, json_len, "{\"id\":\"%s\",\"msg\":\"%s\",\"env\":\"%s\"}", job_id, msg_hex, env_hex); free(msg_hex); free(env_hex); redisContext *c = redisConnect("127.0.0.1", 6379); if (c == NULL || c->err) { if (c) redisFree(c); free(json_payload); return EXIT_TEMP_FAIL; } redisReply *reply = redisCommand(c, "LPUSH qmail_keydb_queue %s", json_payload); free(json_payload); if (reply == NULL) { redisFree(c); return EXIT_TEMP_FAIL; } freeReplyObject(reply); redisFree(c); return EXIT_SUCCESS; } 3. 컴파일 및 시스템 적용 작성한 C 코드를 컴파일하여 기존 qmail-queue 바이너리를 대체합니다. 컴파일 시 -lhiredis 링커 옵션을 반드시 포함해야 합니다. Bash # 1. 원본 qmail-queue 백업 mv /var/qmail/bin/qmail-queue /var/qmail/bin/qmail-queue.orig # 2. KeyDB 전용 qmail-queue 컴파일 (hiredis 링크) gcc -O2 -Wall -o qmail-queue qmail-queue-keydb.c -lhiredis # 3. 바이너리 이동 및 권한 설정 cp qmail-queue /var/qmail/bin/qmail-queue chown qmailq:qmail /var/qmail/bin/qmail-queue chmod 4711 /var/qmail/bin/qmail-queue 이 코드가 적용되면 qmail-smtpd가 메일을 수신하는 즉시 파일 시스템을 거치지 않고 직접 KeyDB 리스트로 바이너리 데이터가 푸시됩니다. 앞서 구성하셨던 백그라운드 워커가 이 큐를 읽어 실제 메일 전송 처리를 담당하게 됩니다.