浅谈oracle 存储后台进程和用户进程的debug trace log(上)

本文详细介绍了Oracle数据库中的用户进程、服务进程及后台进程的功能与作用,包括DBWn、LGWR、SMON等关键进程的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先我们需要了解oracle的一系列用户进程、服务进程、后台进程。

用户进程(User Process)
  是一个需要与
Oracle Server交互的程序
  运行于客户端
  当用户运行某个工具或应用程序(如SQL*Plus)时创建,当用户退出上述程序时结束
  用户进程向
Oracle Server发出调用,但它并不与Oracle Server直接交互,而是通过Server Process与Oracle Server进行交互。
  --------------------------------------------------------------------------------------
  服务进程(Server Process)
  是一个直接与Oracle Server交互的程序。
  与Oracle Server运行于同一台机器上
  使用PGA执行User Process发出的调用,并向User Process返回结果状态和结果信息。
  --------------------------------------------------------------------------------------
  后台进程(Background Process)注:本人认为比较重要的一部分 。
  后台进程用于维护物理
存储与内存中的数据之间的关系。主要包括:
  DBWn
  PMON
  CKPT
  LGWR
  SMON
  RECO
  ARCn
  -----------------------------------------
  Database Writer(DBWn)
  DBWn在以下情况将数据写入磁盘(数据文件data file):
  1)发生检查点
  2)达到脏缓冲区阈值
  3)没有可用的缓冲区
  4)超时
  5)将表空间设置为脱机或只读
  6)删除或截断表
  7)备份表空间
  -----------------------------------------
  LOG Writer(LGWR)
  LGWR在以下情况将缓冲区中的数据写入磁盘(联机日志文件redo log):
  1)事务提交
  2)三分之一的redo日志缓冲区已满
  3)Redo日志缓冲区中的内容超过1M
  4)每三秒钟
  4)在DBWn写磁盘之前
  -----------------------------------------
  System Monitor(SMON)
  负责检查和维护Oracle database的一致性。它主要完成以下工作:
  1)实例恢复
  2)重做已提交的事务
  3)打开数据库
  4)回滚未提交的事务
  5)合并数据文件中相邻的自由空间
  6)释放临时段的空间
  ----------------------------------------
  Process Monitor(PMON)
  当某个进程失效时,清除相关的资源。它主要负责以下工作:
  1)回滚用户的当前事务
  2)释放相关的锁
  3)是否其它相关的资源
  ---------------------------------------
  Checkpoint(CKPT)
  Checkpoint负责:
  1)使DBWn将SGA中所有被修改的数据库缓冲区的内容写入磁盘,无论事务是否被提交
  2)用检查点信息更新数据文件头
  3)用检查点信息更新控制文件
  它可以保证:
  1)将经常被修改的数据块写入磁盘
  2)简化实例恢复
  ---------------------------------------
  Archiver(ARCn)
  是一个可选的后台进程
  当把数据库设置为ARCHIVELOG模式时,可以自动归档联机redo日志
  能够保存所有对数据库所做修改的记录

一个Oracle系统的进程可以分为两个主要的组:
1.用户进程运行应用程序或者Oracle工具代码
2.Oracle进程运行Oracle数据库服务代码。它们包括服务器进程和后台进程。
进程结构针对不同的Oracle配置、不同的操作系统和Oracle附件的选择都会有所不同。连接用户的代码可以配置为共享服务器或者专用服务器。
在专用服务器情况下,对每个用户来说,数据库应用程序运行的进程(用户进程)和Oracle数据库服务器代码运行的进程(专用服务器进程)不同。
在共享服务器下,数据库应用程序运行在和Oracle数据库代码不同的进程上(用户进程)。每个运行Oracle服务器代码的服务器进程(共享服务器进程)可以对多个用户进程服务。

服务器进程
Oracle创建服务器进程来处理连接到这个实例的用户进程的请求。在应用程序和Oracle运行在一台机器的情况下,可以将用户进程和对应的服务器进程合并来降低系统开销。但是,当应用程序和Oracle在不同的计算机上运行时,用户进程总是通过不同的服务器进程连接Oracle。
为每个用户应用程序创建的服务器进程(或者合并的用户/服务器进程的服务器部分)可以执行下列任务:
1,解析和运行应用程序发布的SQL语句
2,如果SGA中不存在一些数据块的数据,服务器进程会从磁盘上的数据文件读取必要的数据块到SGA的共享服务器缓存中。
3,以应用程序可以处理的信息方式返回结果。
后台进程
为获得最高的性能和适应多用户,一个多进程Oracle系统使用一些叫做后台进程的额外Oracle进程。
一个Oracle实例可以有很多后台进程;但不是所有的都必须存在。后台进程数量很多。查看视图V$BGPROCESS可以得到关于后台进程的信息。一个Oracle实例中的后台进程可以包含下列进程:
1,数据库写入进程(Database Writer Process,DBWn)
2,日志写入进程(Log Writer Process,LGWR)
3,检查点进程(Checkpoint  Process,CKPT)
4,系统监视进程(System Monitor Process,SMON)
5,进程监视进程(Process Monitor Process,PMON)
6,恢复进程(Recoverer Process,RECO)
7,任务队列进程(Job Queue Processes)
8,归档进程(ARCn)
9,队列监控进程(Queue Monitor Processes,QMNn)
10,其他后台进程
在许多操作系统上,后台进程在实例启动时自动创建。

<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官网下载BasicSDK包 # 编译 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密钥轮换策略
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值