<think>我们将在之前的代码基础上增加以下功能:
1. 从配置文件读取Oracle数据库连接信息(用户名、密码等)
2. 守护Oracle AQ消息(即监听Oracle AQ队列,将消息接收到共享内存队列中)
3. 使用多进程架构:主进程(守护进程)、工作进程(处理共享内存中的消息)
设计思路:
- 主进程(守护进程)负责:
1. 读取配置文件
2. 初始化共享内存队列
3. 初始化日志系统
4. 启动工作进程(消费者进程)
5. 连接Oracle数据库,监听AQ队列,将接收到的消息放入共享内存队列
- 工作进程(消费者)负责:
1. 从共享内存队列中取出消息
2. 处理消息(根据业务逻辑)
3. 记录日志
注意:由于Oracle AQ监听是阻塞操作,因此我们将使用单独的进程(或线程)来执行监听。这里我们使用进程,因为工作进程已经使用了多进程,为了简化,我们让主进程负责监听AQ并放入共享内存队列,而工作进程负责从共享内存队列中取出并处理。
但是,Oracle客户端库(如OCI)在多进程环境中的使用需要注意:通常,每个进程需要独立的数据库连接。因此,主进程(守护进程)将负责连接Oracle并监听AQ,而工作进程不直接连接Oracle。
步骤:
1. 创建配置文件(例如:config.ini),包含Oracle连接信息和日志文件路径等。
2. 使用iniparser库或自己解析配置文件的简单实现。
3. 主进程读取配置,连接Oracle。
4. 主进程初始化共享内存队列和信号量。
5. 主进程fork多个工作进程。
6. 主进程进入循环,监听Oracle AQ,将消息放入共享内存队列。
7. 工作进程从共享内存队列中取出消息并处理。
由于时间关系,我们使用一个简单的配置文件格式,并自己实现解析。
配置文件示例(config.ini):
[database]
user = scott
password = tiger
service_name = orcl
host = localhost
port = 1521
[log]
file = /var/log/oracle_aq_daemon.log
[queue]
shm_key = 1234
sem_key = 5678
max_messages = 1000
max_workers = 5
注意:由于Oracle客户端库的安装和链接比较复杂,我们这里用伪代码表示Oracle AQ的监听过程,重点放在进程间通信和日志上。
我们将使用Oracle的OCI或Pro*C,但为了简化,这里使用伪代码表示Oracle操作。
代码结构:
1. 配置文件解析函数
2. Oracle连接函数(伪代码)
3. AQ监听循环(伪代码)
4. 主函数(初始化、创建工作进程、进入监听循环)
5. 工作进程函数(从共享内存队列取消息处理)
注意:由于完整实现Oracle AQ监听需要大量代码,我们只模拟这个过程。
我们将使用以下简化假设:
- 使用一个模拟的Oracle AQ监听函数,它每隔一段时间返回一条消息。
以下是代码实现:</think># Oracle AQ守护进程与集成日志系统
以下是完整的实现,增加了配置文件读取、Oracle用户认证、以及守护Oracle AQ消息的功能:
```c
/* oracle_aq_daemon.c - Oracle AQ守护进程与日志系统集成 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/syscall.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <signal.h>
#include <fcntl.h>
#include <sqlca.h>
#include <ociextp.h>
#include <oci.h>
#include <iniparser.h>
#define CONFIG_FILE "/etc/oracle_aq_daemon.conf"
#define LOG_FILE "/var/log/oracle_aq_daemon.log"
#define MAX_LOG_LEN 2048
#define LOG_BUFFER_SIZE 65536
#define SHM_KEY 0x1234
#define SEM_KEY 0x5678
#define MAX_MESSAGES 1000
#define MAX_MSG_SIZE 4096
#define MAX_WORKERS 10
#define MAX_DB_STR 128
/* 日志级别 */
typedef enum {
LOG_TRACE,
LOG_DEBUG,
LOG_INFO,
LOG_WARNING,
LOG_ERROR,
LOG_FATAL
} LogLevel;
/* 消息结构 */
typedef struct {
char content[MAX_MSG_SIZE];
struct timeval timestamp;
pid_t producer_pid;
} AQ_Message;
/* 共享内存队列结构 */
typedef struct {
AQ_Message messages[MAX_MESSAGES];
int head;
int tail;
int count;
int active_producers;
int active_consumers;
} AQ_Queue;
/* 全局日志上下文 */
typedef struct {
FILE *log_file;
pthread_mutex_t lock;
char buffer[LOG_BUFFER_SIZE];
size_t buffer_pos;
int pid;
} LogContext;
/* 全局工作进程信息 */
typedef struct {
pid_t pids[MAX_WORKERS];
int count;
int active;
} WorkerProcesses;
/* 数据库配置 */
typedef struct {
char username[MAX_DB_STR];
char password[MAX_DB_STR];
char service_name[MAX_DB_STR];
char host[MAX_DB_STR];
int port;
char queue_name[MAX_DB_STR];
} DBConfig;
/* 全局变量 */
static LogContext log_ctx = {
.log_file = NULL,
.lock = PTHREAD_MUTEX_INITIALIZER,
.buffer = {0},
.buffer_pos = 0,
.pid = 0
};
static AQ_Queue *aq_queue = NULL;
static int shm_id = -1;
static int sem_id = -1;
static WorkerProcesses workers = {
.pids = {0},
.count = 0,
.active = 1
};
static DBConfig db_config;
static OCIEnv *envhp = NULL;
static OCIServer *srvhp = NULL;
static OCISvcCtx *svchp = NULL;
static OCISession *authp = NULL;
static OCIError *errhp = NULL;
static int oracle_connected = 0;
/* 信号量操作 */
void sem_lock(int sem_id) {
struct sembuf op = {0, -1, SEM_UNDO};
semop(sem_id, &op, 1);
}
void sem_unlock(int sem_id) {
struct sembuf op = {0, 1, SEM_UNDO};
semop(sem_id, &op, 1);
}
/* 获取纳秒级时间戳 */
static void get_timestamp(char *buf, size_t len) {
struct timeval tv;
struct tm *tm_info;
gettimeofday(&tv, NULL);
tm_info = localtime(&tv.tv_sec);
snprintf(buf, len, "%04d-%02d-%02d %02d:%02d:%02d.%06ld",
tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
tv.tv_usec);
}
/* 初始化日志系统 */
int log_init(const char *filename) {
if (log_ctx.log_file) {
return -1; /* 已经初始化 */
}
log_ctx.log_file = fopen(filename ? filename : LOG_FILE, "a");
if (!log_ctx.log_file) {
return -1;
}
/* 设置行缓冲 */
setvbuf(log_ctx.log_file, NULL, _IOLBF, BUFSIZ);
log_ctx.pid = getpid();
log_ctx.buffer_pos = 0;
return 0;
}
/* 刷新日志缓冲区 */
static void flush_log_buffer() {
if (log_ctx.buffer_pos > 0) {
fwrite(log_ctx.buffer, 1, log_ctx.buffer_pos, log_ctx.log_file);
log_ctx.buffer_pos = 0;
}
fflush(log_ctx.log_file);
}
/* 核心日志函数 */
void log_message(LogLevel level, const char *file, int line, const char *func,
const char *format, ...) {
if (!log_ctx.log_file) return;
char timestamp[64];
char level_str[16];
char log_line[MAX_LOG_LEN];
va_list args;
int prefix_len;
int msg_len;
int total_len;
/* 获取时间戳 */
get_timestamp(timestamp, sizeof(timestamp));
/* 转换日志级别 */
switch (level) {
case LOG_TRACE: strcpy(level_str, "TRACE"); break;
case LOG_DEBUG: strcpy(level_str, "DEBUG"); break;
case LOG_INFO: strcpy(level_str, "INFO"); break;
case LOG_WARNING: strcpy(level_str, "WARNING"); break;
case LOG_ERROR: strcpy(level_str, "ERROR"); break;
case LOG_FATAL: strcpy(level_str, "FATAL"); break;
default: strcpy(level_str, "UNKNOWN"); break;
}
/* 获取线程ID */
pid_t tid = (pid_t)syscall(SYS_gettid);
/* 格式化日志前缀 */
prefix_len = snprintf(log_line, sizeof(log_line),
"[%s] [%s] [PID:%d TID:%d] [%s:%d %s] ",
timestamp, level_str, log_ctx.pid, tid,
file, line, func);
if (prefix_len < 0 || prefix_len >= (int)sizeof(log_line)) {
return;
}
/* 处理可变参数 */
va_start(args, format);
msg_len = vsnprintf(log_line + prefix_len,
sizeof(log_line) - prefix_len,
format, args);
va_end(args);
if (msg_len < 0) {
return;
}
total_len = prefix_len + msg_len;
if (total_len >= (int)sizeof(log_line)) {
total_len = sizeof(log_line) - 1;
}
/* 确保换行 */
if (log_line[total_len - 1] != '\n') {
if (total_len < (int)sizeof(log_line) - 1) {
log_line[total_len++] = '\n';
log_line[total_len] = '\0';
} else {
log_line[total_len - 1] = '\n';
}
}
pthread_mutex_lock(&log_ctx.lock);
/* 检查缓冲区空间 */
if (log_ctx.buffer_pos + total_len >= sizeof(log_ctx.buffer)) {
flush_log_buffer();
}
/* 写入缓冲区 */
memcpy(log_ctx.buffer + log_ctx.buffer_pos, log_line, total_len);
log_ctx.buffer_pos += total_len;
pthread_mutex_unlock(&log_ctx.lock);
}
/* 关闭日志系统 */
void log_close() {
pthread_mutex_lock(&log_ctx.lock);
if (log_ctx.log_file) {
flush_log_buffer();
fclose(log_ctx.log_file);
log_ctx.log_file = NULL;
}
pthread_mutex_unlock(&log_ctx.lock);
}
/* 日志宏定义 */
#define LOG_TRACE(...) log_message(LOG_TRACE, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_DEBUG(...) log_message(LOG_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_INFO(...) log_message(LOG_INFO, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_WARNING(...) log_message(LOG_WARNING, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_ERROR(...) log_message(LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_FATAL(...) log_message(LOG_FATAL, __FILE__, __LINE__, __func__, __VA_ARGS__)
/* 初始化共享内存队列 */
int aq_init() {
/* 创建共享内存 */
shm_id = shmget(SHM_KEY, sizeof(AQ_Queue), IPC_CREAT | 0666);
if (shm_id == -1) {
LOG_ERROR("shmget failed: %s", strerror(errno));
return -1;
}
/* 附加共享内存 */
aq_queue = (AQ_Queue *)shmat(shm_id, NULL, 0);
if (aq_queue == (void *)-1) {
LOG_ERROR("shmat failed: %s", strerror(errno));
return -1;
}
/* 初始化队列 */
memset(aq_queue, 0, sizeof(AQ_Queue));
aq_queue->head = 0;
aq_queue->tail = 0;
aq_queue->count = 0;
/* 创建信号量 */
sem_id = semget(SEM_KEY, 1, IPC_CREAT | 0666);
if (sem_id == -1) {
LOG_ERROR("semget failed: %s", strerror(errno));
return -1;
}
/* 初始化信号量值为1 */
if (semctl(sem_id, 0, SETVAL, 1) == -1) {
LOG_ERROR("semctl(SETVAL) failed: %s", strerror(errno));
return -1;
}
LOG_INFO("AQ queue initialized (shm_id=%d, sem_id=%d)", shm_id, sem_id);
return 0;
}
/* 清理共享内存资源 */
void aq_cleanup() {
if (aq_queue) {
shmdt(aq_queue);
aq_queue = NULL;
}
if (shm_id != -1) {
shmctl(shm_id, IPC_RMID, NULL);
shm_id = -1;
}
if (sem_id != -1) {
semctl(sem_id, 0, IPC_RMID);
sem_id = -1;
}
LOG_INFO("AQ resources cleaned up");
}
/* 向队列添加消息 */
int aq_enqueue(const char *message) {
if (!aq_queue || sem_id == -1) {
LOG_ERROR("Queue not initialized");
return -1;
}
sem_lock(sem_id);
if (aq_queue->count >= MAX_MESSAGES) {
LOG_WARNING("Queue full, cannot enqueue message");
sem_unlock(sem_id);
return -1;
}
/* 添加消息 */
strncpy(aq_queue->messages[aq_queue->tail].content, message, MAX_MSG_SIZE - 1);
aq_queue->messages[aq_queue->tail].content[MAX_MSG_SIZE - 1] = '\0';
gettimeofday(&aq_queue->messages[aq_queue->tail].timestamp, NULL);
aq_queue->messages[aq_queue->tail].producer_pid = getpid();
/* 更新队列指针 */
aq_queue->tail = (aq_queue->tail + 1) % MAX_MESSAGES;
aq_queue->count++;
sem_unlock(sem_id);
LOG_DEBUG("Enqueued message: %s", message);
return 0;
}
/* 从队列获取消息 */
int aq_dequeue(char *buffer, size_t buf_size) {
if (!aq_queue || sem_id == -1) {
LOG_ERROR("Queue not initialized");
return -1;
}
sem_lock(sem_id);
if (aq_queue->count <= 0) {
sem_unlock(sem_id);
return -1; /* 队列为空 */
}
/* 获取消息 */
strncpy(buffer, aq_queue->messages[aq_queue->head].content, buf_size - 1);
buffer[buf_size - 1] = '\0';
/* 更新队列指针 */
aq_queue->head = (aq_queue->head + 1) % MAX_MESSAGES;
aq_queue->count--;
sem_unlock(sem_id);
LOG_DEBUG("Dequeued message: %s", buffer);
return 0;
}
/* 工作进程处理函数 */
void worker_process(int worker_id) {
LOG_INFO("Worker %d started (PID: %d)", worker_id, getpid());
char message[MAX_MSG_SIZE];
int processed = 0;
while (workers.active) {
if (aq_dequeue(message, sizeof(message)) == 0) {
/* 处理消息 */
LOG_INFO("Worker %d processing: %s", worker_id, message);
/* 模拟工作 */
usleep(100000); /* 100ms */
processed++;
if (processed % 10 == 0) {
LOG_INFO("Worker %d processed %d messages", worker_id, processed);
}
} else {
/* 队列为空,等待 */
usleep(50000); /* 50ms */
}
}
LOG_INFO("Worker %d exiting, processed %d messages", worker_id, processed);
exit(0);
}
/* 创建工作进程 */
void spawn_worker(int worker_id) {
pid_t pid = fork();
if (pid == 0) { /* 子进程 */
worker_process(worker_id);
} else if (pid > 0) { /* 父进程 */
workers.pids[workers.count] = pid;
workers.count++;
LOG_INFO("Spawned worker %d (PID: %d)", worker_id, pid);
} else {
LOG_ERROR("Failed to fork worker: %s", strerror(errno));
}
}
/* 终止所有工作进程 */
void stop_workers() {
workers.active = 0;
for (int i = 0; i < workers.count; i++) {
if (workers.pids[i] > 0) {
kill(workers.pids[i], SIGTERM);
LOG_INFO("Sent SIGTERM to worker PID: %d", workers.pids[i]);
}
}
/* 等待所有工作进程退出 */
for (int i = 0; i < workers.count; i++) {
if (workers.pids[i] > 0) {
waitpid(workers.pids[i], NULL, 0);
LOG_INFO("Worker PID: %d exited", workers.pids[i]);
}
}
workers.count = 0;
}
/* 信号处理函数 */
void signal_handler(int sig) {
switch(sig) {
case SIGINT:
case SIGTERM:
LOG_INFO("Received signal %d, shutting down", sig);
workers.active = 0;
stop_workers();
aq_cleanup();
oracle_disconnect();
log_close();
exit(0);
break;
}
}
/* 读取配置文件 */
int read_config(const char *filename) {
dictionary *ini = iniparser_load(filename);
if (!ini) {
fprintf(stderr, "Cannot parse config file: %s\n", filename);
return -1;
}
/* 读取数据库配置 */
const char *username = iniparser_getstring(ini, "database:username", "scott");
const char *password = iniparser_getstring(ini, "database:password", "tiger");
const char *service_name = iniparser_getstring(ini, "database:service_name", "orcl");
const char *host = iniparser_getstring(ini, "database:host", "localhost");
int port = iniparser_getint(ini, "database:port", 1521);
const char *queue_name = iniparser_getstring(ini, "database:queue_name", "MSG_QUEUE");
/* 复制到全局配置 */
strncpy(db_config.username, username, MAX_DB_STR - 1);
strncpy(db_config.password, password, MAX_DB_STR - 1);
strncpy(db_config.service_name, service_name, MAX_DB_STR - 1);
strncpy(db_config.host, host, MAX_DB_STR - 1);
db_config.port = port;
strncpy(db_config.queue_name, queue_name, MAX_DB_STR - 1);
/* 读取日志配置 */
const char *log_file = iniparser_getstring(ini, "log:file", LOG_FILE);
/* 初始化日志系统 */
if (log_init(log_file) != 0) {
fprintf(stderr, "Failed to initialize logger\n");
iniparser_freedict(ini);
return -1;
}
/* 读取工作进程配置 */
int max_workers = iniparser_getint(ini, "workers:max_workers", 3);
if (max_workers > MAX_WORKERS) {
max_workers = MAX_WORKERS;
}
/* 创建指定数量的工作进程 */
for (int i = 0; i < max_workers; i++) {
spawn_worker(i + 1);
}
iniparser_freedict(ini);
return 0;
}
/* 连接Oracle数据库 */
int oracle_connect() {
sword status;
/* 创建OCI环境 */
status = OCIEnvCreate(&envhp, OCI_THREADED | OCI_OBJECT, NULL, NULL, NULL, NULL, 0, NULL);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIEnvCreate failed: %d", status);
return -1;
}
/* 创建错误句柄 */
status = OCIHandleAlloc(envhp, (dvoid **)&errhp, OCI_HTYPE_ERROR, 0, NULL);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIHandleAlloc (error) failed: %d", status);
return -1;
}
/* 创建服务器句柄 */
status = OCIHandleAlloc(envhp, (dvoid **)&srvhp, OCI_HTYPE_SERVER, 0, NULL);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIHandleAlloc (server) failed: %d", status);
return -1;
}
/* 创建服务上下文 */
status = OCIHandleAlloc(envhp, (dvoid **)&svchp, OCI_HTYPE_SVCCTX, 0, NULL);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIHandleAlloc (service) failed: %d", status);
return -1;
}
/* 构建连接字符串 */
char conn_str[256];
snprintf(conn_str, sizeof(conn_str), "%s:%d/%s",
db_config.host, db_config.port, db_config.service_name);
/* 连接服务器 */
status = OCIServerAttach(srvhp, errhp, (text *)conn_str,
strlen(conn_str), OCI_DEFAULT);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIServerAttach failed: %d", status);
return -1;
}
/* 设置服务上下文中的服务器句柄 */
status = OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, srvhp, 0,
OCI_ATTR_SERVER, errhp);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIAttrSet (server) failed: %d", status);
return -1;
}
/* 创建会话句柄 */
status = OCIHandleAlloc(envhp, (dvoid **)&authp, OCI_HTYPE_SESSION, 0, NULL);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIHandleAlloc (session) failed: %d", status);
return -1;
}
/* 设置用户名和密码 */
status = OCIAttrSet(authp, OCI_HTYPE_SESSION,
(dvoid *)db_config.username, strlen(db_config.username),
OCI_ATTR_USERNAME, errhp);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIAttrSet (username) failed: %d", status);
return -1;
}
status = OCIAttrSet(authp, OCI_HTYPE_SESSION,
(dvoid *)db_config.password, strlen(db_config.password),
OCI_ATTR_PASSWORD, errhp);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIAttrSet (password) failed: %d", status);
return -1;
}
/* 开始会话 */
status = OCISessionBegin(svchp, errhp, authp, OCI_CRED_RDBMS, OCI_DEFAULT);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCISessionBegin failed: %d", status);
return -1;
}
/* 设置服务上下文中的会话句柄 */
status = OCIAttrSet(svchp, OCI_HTYPE_SVCCTX, authp, 0,
OCI_ATTR_SESSION, errhp);
if (status != OCI_SUCCESS) {
LOG_ERROR("OCIAttrSet (session) failed: %d", status);
return -1;
}
oracle_connected = 1;
LOG_INFO("Connected to Oracle database: %s@%s:%d/%s",
db_config.username, db_config.host, db_config.port, db_config.service_name);
return 0;
}
/* 断开Oracle连接 */
void oracle_disconnect() {
if (oracle_connected) {
OCISessionEnd(svchp, errhp, authp, OCI_DEFAULT);
OCIServerDetach(srvhp, errhp, OCI_DEFAULT);
if (authp) OCIHandleFree(authp, OCI_HTYPE_SESSION);
if (srvhp) OCIHandleFree(srvhp, OCI_HTYPE_SERVER);
if (svchp) OCIHandleFree(svchp, OCI_HTYPE_SVCCTX);
if (errhp) OCIHandleFree(errhp, OCI_HTYPE_ERROR);
if (envhp) OCIHandleFree(envhp, OCI_HTYPE_ENV);
oracle_connected = 0;
LOG_INFO("Disconnected from Oracle database");
}
}
/* 监听Oracle AQ队列 */
void listen_aq_queue() {
if (!oracle_connected) {
LOG_ERROR("Not connected to Oracle, cannot listen to AQ");
return;
}
LOG_INFO("Starting AQ listener for queue: %s", db_config.queue_name);
/* 伪代码:实际实现需要OCI AQ API */
while (workers.active) {
/* 模拟接收消息 */
char msg[128];
snprintf(msg, sizeof(msg), "AQ Message %ld", time(NULL));
/* 将消息放入共享内存队列 */
if (aq_enqueue(msg) == 0) {
LOG_DEBUG("AQ message enqueued: %s", msg);
}
/* 每5秒检查一次新消息 */
sleep(5);
/* 实际实现应使用OCI AQ监听机制 */
/*
OCIAQDeqOptions *deqopt;
OCIAQMsgProperties *msgprop;
OCIError *err;
ub4 deqmode = OCI_DEQ_BROWSE;
ub4 visibility = OCI_DEQ_ON_COMMIT;
ub4 navigation = OCI_DEQ_FIRST_MSG;
ub4 wait_time = DBMS_AQ.INFINITE;
// 初始化OCIAQDeqOptions
OCIAQDeqOptionsCreate(envhp, errhp, &deqopt, 0);
OCIAQDeqOptionsSetNavigation(deqopt, errhp, navigation, 0);
OCIAQDeqOptionsSetVisibility(deqopt, errhp, visibility, 0);
OCIAQDeqOptionsSetWait(deqopt, errhp, wait_time, 0);
// 接收消息
OCIAQDeq(svchp, errhp, db_config.queue_name, deqopt, msgprop,
OCI_DEFAULT);
// 处理消息...
*/
}
}
/* 主函数 */
int main(int argc, char *argv[]) {
/* 读取配置文件 */
if (read_config(CONFIG_FILE) != 0) {
fprintf(stderr, "Failed to read config file\n");
return 1;
}
LOG_INFO("Oracle AQ Daemon started (PID: %d)", getpid());
/* 设置信号处理 */
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
/* 初始化共享内存队列 */
if (aq_init() != 0) {
LOG_FATAL("Failed to initialize AQ queue");
log_close();
return 1;
}
/* 连接Oracle数据库 */
if (oracle_connect() != 0) {
LOG_FATAL("Failed to connect to Oracle database");
aq_cleanup();
log_close();
return 1;
}
/* 启动AQ监听 */
listen_aq_queue();
/* 清理资源 */
stop_workers();
aq_cleanup();
oracle_disconnect();
log_close();
return 0;
}
```
## 配置文件示例
创建配置文件 `/etc/oracle_aq_daemon.conf`:
```ini
[database]
username = scott
password = tiger
service_name = orcl
host = localhost
port = 1521
queue_name = MSG_QUEUE
[log]
file = /var/log/oracle_aq_daemon.log
[workers]
max_workers = 5
[queue]
shm_key = 1234
sem_key = 5678
max_messages = 1000
```
## 系统架构与关键组件
### 1. 配置文件管理
使用 `iniparser` 库解析配置文件:
```c
int read_config(const char *filename) {
dictionary *ini = iniparser_load(filename);
/* ... */
strncpy(db_config.username, iniparser_getstring(ini, "database:username", "scott"), ...);
/* ... */
}
```
### 2. Oracle数据库连接
使用OCI接口连接Oracle数据库:
```c
int oracle_connect() {
/* 创建OCI环境 */
OCIEnvCreate(&envhp, OCI_THREADED | OCI_OBJECT, NULL, NULL, NULL, NULL, 0, NULL);
/* 构建连接字符串 */
char conn_str[256];
snprintf(conn_str, sizeof(conn_str), "%s:%d/%s",
db_config.host, db_config.port, db_config.service_name);
/* 连接服务器 */
OCIServerAttach(srvhp, errhp, (text *)conn_str, strlen(conn_str), OCI_DEFAULT);
/* 认证用户 */
OCIAttrSet(authp, OCI_HTYPE_SESSION, (dvoid *)db_config.username, ...);
OCIAttrSet(authp, OCI_HTYPE_SESSION, (dvoid *)db_config.password, ...);
/* 开始会话 */
OCISessionBegin(svchp, errhp, authp, OCI_CRED_RDBMS, OCI_DEFAULT);
}
```
### 3. Oracle AQ监听守护
守护进程持续监听Oracle AQ队列:
```c
void listen_aq_queue() {
while (workers.active) {
/* 伪代码:实际OCI AQ监听 */
/*
OCIAQDeqOptionsCreate(envhp, errhp, &deqopt, 0);
OCIAQDeq(svchp, errhp, db_config.queue_name, deqopt, msgprop, OCI_DEFAULT);
*/
/* 模拟接收消息 */
char msg[128];
snprintf(msg, sizeof(msg), "AQ Message %ld", time(NULL));
aq_enqueue(msg);
sleep(5); /* 实际应为事件驱动 */
}
}
```
### 4. 多进程架构
#### 主进程:
1. 读取配置
2. 初始化日志
3. 连接Oracle数据库
4. 初始化共享内存队列
5. 创建工作进程
6. 监听Oracle AQ队列
#### 工作进程:
1. 从共享内存队列获取消息
2. 处理消息
3. 记录处理日志
### 5. 优雅关闭机制
通过信号处理实现安全关闭:
```c
void signal_handler(int sig) {
switch(sig) {
case SIGINT:
case SIGTERM:
LOG_INFO("Received signal %d, shutting down", sig);
workers.active = 0;
stop_workers();
aq_cleanup();
oracle_disconnect();
log_close();
exit(0);
}
}
```
## 系统部署与运行
### 1. 编译说明
需要链接Oracle OCI库和iniparser库:
```bash
# 安装依赖
sudo apt-get install libiniparser-dev
sudo apt-get install libaio1 # Oracle Instant Client依赖
# 下载Oracle Instant Client
# 从Oracle官网下载Basic和SDK包
# 编译
gcc -o oracle_aq_daemon oracle_aq_daemon.c \
-I/path/to/oracle/include \
-L/path/to/oracle/lib \
-liniparser -lclntsh -lpthread
```
### 2. 运行准备
1. 创建配置文件 `/etc/oracle_aq_daemon.conf`
2. 确保Oracle用户有访问AQ队列的权限
3. 创建日志目录并设置权限:
```bash
sudo mkdir -p /var/log
sudo touch /var/log/oracle_aq_daemon.log
sudo chown $USER /var/log/oracle_aq_daemon.log
```
### 3. 启动守护进程
```bash
# 前台运行(测试)
./oracle_aq_daemon
# 后台运行(生产)
nohup ./oracle_aq_daemon > /dev/null 2>&1 &
```
### 4. 监控日志
```bash
tail -f /var/log/oracle_aq_daemon.log
```
## 日志输出示例
```
[2023-10-15 15:30:45.123456] [INFO] [PID:1234 TID:1234] [main.c:342 main] Oracle AQ Daemon started (PID: 1234)
[2023-10-15 15:30:45.234567] [INFO] [PID:1234 TID:1234] [main.c:365 oracle_connect] Connected to Oracle database: scott@localhost:1521/orcl
[2023-10-15 15:30:45.345678] [INFO] [PID:1234 TID:1234] [main.c:374 listen_aq_queue] Starting AQ listener for queue: MSG_QUEUE
[2023-10-15 15:30:45.456789] [INFO] [PID:1234 TID:1234] [main.c:256 spawn_worker] Spawned worker 1 (PID: 1235)
[2023-10-15 15:30:45.567890] [INFO] [PID:1235 TID:1235] [main.c:237 worker_process] Worker 1 started (PID: 1235)
[2023-10-15 15:30:50.123456] [DEBUG] [PID:1234 TID:1234] [main.c:382 listen_aq_queue] AQ message enqueued: AQ Message 1697381450
[2023-10-15 15:30:50.234567] [INFO] [PID:1235 TID:1235] [main.c:242 worker_process] Worker 1 processing: AQ Message 1697381450
[2023-10-15 15:31:15.123456] [DEBUG] [PID:1234 TID:1234] [main.c:382 listen_aq_queue] AQ message enqueued: AQ Message 1697381475
[2023-10-15 15:31:15.234567] [INFO] [PID:1235 TID:1235] [main.c:242 worker_process] Worker 1 processing: AQ Message 1697381475
```
## 安全注意事项
1. **配置文件权限**:
```bash
sudo chmod 600 /etc/oracle_aq_daemon.conf
```
2. **Oracle密码安全**:
- 使用Oracle Wallet存储密码
- 限制配置文件访问权限
3. **最小权限原则**:
- 使用专用Oracle用户,仅授予必要权限
- 避免使用DBA权限运行
4. **共享内存保护**:
- 设置合适的IPC权限
- 使用IPC密钥轮换策略