This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
#include "DataAccess.h"
CDataAccess::CDataAccess()
{
// 멤버 변수 초기화
m_uResourceId = 0;
m_lAccessTime = 0;
}
CDataAccess::~CDataAccess()
{
}
std::string CDataAccess::GetQueryURI( void )
{
// URI의 '(작은따옴표) 문자가 있을 경우 SQL 오류 방지를 위하여 관련 문자를 다르게 변경함
std::string strUri = ReplaceString( m_strUri, "'", "''" );
return strUri;
}
std::string CDataAccess::ReplaceString( const std::string & source, const std::string search, const std::string replacement )
{
std::string r = source;
std::string::size_type pos = 0;
while( ( pos = r.find( search, pos ) ) != std::string::npos )
{
r.replace( pos, search.size(), replacement );
pos = pos + replacement.size();
}
return r;
}
+67
View File
@@ -0,0 +1,67 @@
/****************************************************************************
rc_accessd data header
-----------------------------------------
begin : 2021/04/07
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __DATA_ACCESS_H__
#define __DATA_ACCESS_H__
#include <string>
// acccess time 추출 관련 각 content 에 대한 세부 정보를 저장하기 위한 data class
class CDataAccess
{
public:
// 생성자
CDataAccess();
// 소멸자
~CDataAccess();
// URI 정보는 특수문자 포함될 수 있어.. Query 에서 사용시 변환 처리 함수
std::string GetQueryURI( void );
public:
// content 정보
unsigned long long m_uResourceId; // resource_id
std::string m_strUri; // uri 정보
std::string m_strHostname; // host_name
std::string m_strFilenamehash; // filename_hash
// 처리 결과
long m_lFileSize; // content 의 물리 size
long m_lAccessTime; // content 의 access time 추출 결과물
long m_lModifyTime; // content 의 modify time 추출 결과물
long m_lChangeTime; // content 의 change time 추출 결과물
private:
// uri 상의 특수 문자 변경을 위한 함수
std::string ReplaceString( const std::string & source, const std::string search, const std::string replacement );
};
#endif /* __DATA_ACCESS_H__ */
+181
View File
@@ -0,0 +1,181 @@
/****************************************************************************
Data Object Queue Template class Class
-----------------------------------------
begin : 2014/06/13
copyright : (C) 2005 Solbox Inc.
author : 개발팀 스토리지 담당.
email : storage.sd@solbox.com
version : 3.4.0
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
/* 참고사항
* 본 Template Class 는 rc_rmcd 의 DataQueue.hpp 에 정의된 소스로서
* rc_rmcd revision 1428 에서 복사한 코드임.
*/
#ifndef __DATA_QUEUE_HPP__
#define __DATA_QUEUE_HPP__
#include <queue>
#include <pthread.h>
// Queue default size
#define QUEUE_DEFAULT_MAX_SIZE 1024
template <typename T>
class CDataQueue
{
private:
// data 객체를 저장할 queue.
std::queue<T> m_queue;
// queue 접근을 제어할 뮤텍스 lock.
pthread_mutex_t m_mutexQueue;
// queue 의 최대 크기 제한값 저장 변수.
unsigned int m_uMaxSize;
public:
// 생성자
CDataQueue() {
m_uMaxSize = QUEUE_DEFAULT_MAX_SIZE;
pthread_mutex_init( &m_mutexQueue, NULL );
}
// 소멸자
~CDataQueue() {
pthread_mutex_destroy( &m_mutexQueue );
}
////////////////////////////////////
// queue 크기를 반환
unsigned int GetSize( void );
// queue 의 push 가능한 크기를 반환
unsigned int GetFreeSize( void );
// queue Max 크기를 반환
unsigned int GetMaxSize( void );
// queue Max 크기를 설정
void SetMaxSize( unsigned int uMaxSize );
// 초기화
void Init( void );
// Data Object 저장
bool Push( T const& object );
// queue 에 저장된 첫번재 element 반환
// - queue 크기가 0 인 경우 호출시 std::out_of_range 오류가 발생해야 하지만...
// 예외 처리를 대부분 하지 않으므로... 빈 객체를 반환처리하도록 함.
// 따라서...꼭 GetSize() 함수를 호출하여 data 가 존재하는지 확인한 후 본 함수 호출할 것.
T Front( void );
// queue 에 저장된 첫번재 element 제거
void Pop( void );
};
///////////////////////////////////////////////
// queue 크기를 반환
template <typename T>
unsigned int CDataQueue<T>::GetSize( void )
{
return (unsigned int)m_queue.size();
}
// queue 의 push 가능한 크기를 반환
template <typename T>
unsigned int CDataQueue<T>::GetFreeSize( void )
{
return ( m_uMaxSize - ( (unsigned int)m_queue.size() ) );
}
// queue Max 크기를 반환
template <typename T>
unsigned int CDataQueue<T>::GetMaxSize( void )
{
return m_uMaxSize;
}
// queue Max 크기를 설정
template <typename T>
void CDataQueue<T>::SetMaxSize( unsigned int uMaxSize )
{
m_uMaxSize = uMaxSize;
}
// 초기화
template <typename T>
void CDataQueue<T>::Init( void )
{
// queue clear 처리를 수행.
// - queue 의 경우 clear 함수가 지원되지 않으므로 size 가 0 이 될때까지 pop 처리
pthread_mutex_lock( &m_mutexQueue );
while( m_queue.empty() == false )
m_queue.pop();
pthread_mutex_unlock( &m_mutexQueue );
return;
}
// Data Object 저장
template <typename T>
bool CDataQueue<T>::Push( T const& object )
{
// 현재 queue size 가 설정된 최대 size 를 이상인 경우 실패 처리.
if( m_queue.size() >= m_uMaxSize )
return false;
else
{
pthread_mutex_lock( &m_mutexQueue );
m_queue.push( object );
pthread_mutex_unlock( &m_mutexQueue );
return true;
}
}
// queue 에 저장된 첫번재 element 반환
template <typename T>
T CDataQueue<T>::Front( void )
{
// queue 특성상 pop, front 처리시 queue 가 비어있지 않음을 보장해야 한다.
// - 만약 queue 가 빈 경우.. std::out_of_range 예외를 발생시켜야 하지만...
// 호출 함수에서 이런 예외를 처리하지 않으므로...
// 빈 깡통 객체를 전달한다.
T object;
if( m_queue.empty() == false )
{
pthread_mutex_lock( &m_mutexQueue );
object = m_queue.front();
pthread_mutex_unlock( &m_mutexQueue );
}
return object;
}
// queue 에 저장된 첫번재 element 제거
template <typename T>
void CDataQueue<T>::Pop( void )
{
// queue 특성상 pop, front 처리시 queue 가 비어있지 않음을 보장해야 한다.
if( m_queue.empty() == false )
{
pthread_mutex_lock( &m_mutexQueue );
m_queue.pop();
pthread_mutex_unlock( &m_mutexQueue );
}
return;
}
#endif /* __DATA_QUEUE_HPP__ */
+184
View File
@@ -0,0 +1,184 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/09
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include "Database.h"
#include "Logger.h"
DataBase::~DataBase()
{
if(m_PGconn != NULL) {
PgCloseDB();
}
}
PGconn *DataBase::PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout)
{
ostringstream conninfo;
// CHG 2014-10-29 huibong
// application_name add to logging RCDB
conninfo << "host=" << strHost << " port=" << Port << " dbname=" << strDBName <<
" user=" << strAcct << " password=" << strPasswd << " connect_timeout=" << timeout <<
" application_name=" << PROG_NAME;
#ifdef _USE_LIBPQ_KEEPALIVE
// This option is supported by at least 9.1.2. and Currently supports Linux systems.
conninfo <<" keepalives=1";
#endif // _USE_LIBPQ_KEEPALIVE
//m_PGconn = PQsetdbLogin(strHost.c_str(), szPort, NULL, NULL, strDBName.c_str(), strAcct.c_str(), strPasswd.c_str());
m_PGconn = PQconnectdb(conninfo.str().c_str());
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
PGconn *DataBase::PgOpenDB(const char *pszDBName)
{
m_PGconn = PQsetdb(NULL, NULL, NULL, NULL, pszDBName);
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
void DataBase::PgCloseDB()
{
if(m_PGconn != NULL)
{
PQfinish(m_PGconn);
m_PGconn = NULL;
}
}
int DataBase::PgResult(CFLAG flag)
{
int fRet = 0;
int Result;
Result = PQresultStatus(m_pRes);
switch(Result) {
case PGRES_EMPTY_QUERY :
fRet = -1;
break;
case PGRES_BAD_RESPONSE :
fRet = -2;
break;
case PGRES_NONFATAL_ERROR :
fRet = -3;
break;
case PGRES_FATAL_ERROR :
fRet = -4;
break;
case PGRES_TUPLES_OK :
fRet = 1;
break;
case PGRES_COMMAND_OK :
fRet = 2;
break;
}
if(fRet < 0) {
m_ErrorMessage = PQresultErrorMessage(m_pRes);
}
m_ResultCode = Result;
if(flag == CLEAR && m_pRes) {
PQclear(m_pRes);
m_pRes = NULL;
}
return fRet;
}
string &DataBase::GetErrorMessage()
{
return m_ErrorMessage;
}
int DataBase::GetCmdTuples()
{
//fprintf(stderr, "DataBase::GetCmdTuples PQcmdTuples %s\n", PQcmdTuples(m_pRes));
return (int)atoi(PQcmdTuples(m_pRes));
}
int DataBase::GetNoTuples()
{
return PQntuples(m_pRes);
}
int DataBase::GetNoFields()
{
return PQnfields(m_pRes);
}
PGresult *DataBase::GetRes()
{
return m_pRes;
}
void DataBase::PgClear()
{
if(m_pRes)
{
PQclear(m_pRes);
m_pRes = NULL;
}
}
char *DataBase::GetValue(int tuple, int field)
{
return PQgetvalue(m_pRes, tuple, field);
}
int DataBase::PgDoExec(char *pszQuery)
{
return this->PgDoExec(pszQuery, NOT_CLEAR);
}
int DataBase::PgDoExec(string &strQuery)
{
return this->PgDoExec((char *)strQuery.c_str(), NOT_CLEAR);
}
int DataBase::PgDoExec(char *pszQuery, CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexec(m_PGconn, pszQuery);
int r = PgResult(flag);
return r;
}
int DataBase::PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexecParams(m_PGconn, pszQuery, nParamCnt, NULL, paramValues, NULL, NULL, 0);
int r = PgResult(flag);
return r;
}
int DataBase::PgEscapeString(char *to, const char *from, size_t length)
{
int retval = 0;
//PQescapeStringConn(m_PGconn, to, from, length, &retval);
PQescapeString(to, from, length);
return retval;
}
+56
View File
@@ -0,0 +1,56 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __POSTGRESQL_DATABASE_H__
#define __POSTGRESQL_DATABASE_H__
#include <iostream>
#include <string>
#include "libpq-fe.h"
#define MaxSizeOfDBQuery 1024*9
using namespace std;
class DataBase
{
public:
enum CFLAG { CLEAR = 1, NOT_CLEAR };
DataBase() { m_PGconn = NULL; m_pRes = NULL;}
~DataBase();
PGconn *PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout = 10);
PGconn *PgOpenDB(const char *pszDBName);
void PgCloseDB();
int PgResult(CFLAG flag);
PGconn *GetPgConn(){ return m_PGconn;}
PGresult *GetRes();
void SetRes(PGresult * v) {m_pRes = v;}
int GetCmdTuples();
int GetNoTuples();
int GetNoFields();
int GetResultCode() { return m_ResultCode; }
char *GetValue(int tuple, int field);
void PgClear();
int PgDoExec(string &strQuery);
int PgDoExec(char *pszQuery);
int PgDoExec(char *pszQuery, CFLAG flag);
int PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag = NOT_CLEAR);
int PgEscapeString(char *to, const char *from, size_t length);
string &GetErrorMessage();
private:
int m_ResultCode;
PGconn *m_PGconn;
PGresult *m_pRes;
string m_ErrorMessage;
};
#endif // ~__POSTGRESQL_DATABASE_H__
+462
View File
@@ -0,0 +1,462 @@
#include "Main.h"
#include "ProcessRename.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "WorkerAccess.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
// Worker 프로세스 중 content access time 정보 추출을 담당할 Worker PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
pid_t g_pidWorkerAccess = -1;
int main( int argc, char * argv[] )
{
// 설정 파일을 저장할 변수.
std::string strConfigFileName = DEFAULT_CONFIG_FILE;
// linux 에서 프로세스를 Titile 변경을 지원을 위해서 argv 메모리에 저장
argv = save_ps_display_args( argc, argv );
// 전달 받은 옵션 여부 확인 및 처리
if( argc >= 2 )
{
int opt;
while( ( opt = getopt( argc, argv, "hvc:" ) ) != -1 ) // 옵션 끝까지 파싱처리
{
switch( opt )
{
case 'h':
case '?': // 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리.
PrintUsage();
return EXIT_SUCCESS;
case 'v':
PrintVersion();
return EXIT_SUCCESS;
case 'c':
strConfigFileName = optarg;
fprintf( stderr, PROG_NAME " start with user define conf[%s]\n", strConfigFileName.c_str() );
break;
default: // 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
return EXIT_FAILURE;
}
}
}
// 프로세스 중복 실행 여부 검사
if( IsCurrentProcessRun() == true )
{
fprintf( stderr, "[warning] Process [" PROG_NAME "] is already running....\n" );
return EXIT_FAILURE;
}
std::string szErrorMessage;
// conf 로딩 및 검사
if( CProcessConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false )
{
fprintf( stderr, "[ERR] %s\n\n", szErrorMessage.c_str() );
return EXIT_FAILURE;
}
// Log 객체 생성 및 초기화
if( CLogger::Init( PROG_NAME, CProcessConfig::GetInstance()->GetLogPath(), LINF ) == false )
{
fprintf( stderr, "[ERR] Log module initialize failed.[%s]\n\n", CProcessConfig::GetInstance()->GetLogPath() );
return EXIT_FAILURE;
}
// Daemonize...
if( daemon( 1, 0 ) == -1 ) // nochdir: true(작업 디렉토리 변경 안함), noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
{
fprintf( stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror( errno ) );
return EXIT_FAILURE;
}
// signal 처리 설정
SetSignalMain();
// Process 기동 관련 정보 기록 -> Log
_LOG( LINF, "***********************************************************" );
_LOG( LINF, " %s Start. Version: %s", PROG_NAME, PROG_VERSION );
_LOG( LINF, "***********************************************************" );
_LOG( LINF, "Config : %s", strConfigFileName.c_str() );
_LOG( LINF, "Log : %s/%s", CProcessConfig::GetInstance()->GetLogPath(), PROG_NAME );
_LOG( LINF, "Log level : %d", CProcessConfig::GetInstance()->GetLogLevel() );
_LOG( LINF, "Access DB : %s %u %s %s", CProcessConfig::GetInstance()->GetRcdbIp()
, CProcessConfig::GetInstance()->GetRcdbPort()
, CProcessConfig::GetInstance()->GetRcdbName()
, CProcessConfig::GetInstance()->GetRcdbAcct() );
_LOG( LINF, "FHS ftsd tcp port : %u", CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() );
_LOG( LINF, "Max queue size : %u", CProcessConfig::GetInstance()->GetMaxQueueSize() );
_LOG( LINF, "Max thread count : %u", CProcessConfig::GetInstance()->GetMaxThreadCount() );
_LOG( LINF, "Access time extract service : %u", CProcessConfig::GetInstance()->GetAccessTimeExtractService() );
_LOG( LINF, "***********************************************************" );
// Log Level 재설정. -> conf 설정대로 변경 처리.
#ifdef _DEBUG_
CLogger::GetInstance()->SetLogLevel( LDBG );
#else
CLogger::GetInstance()->SetLogLevel( CProcessConfig::GetInstance()->GetLogLevel() );
#endif
// Worker Process 생성
// content access time 추출 처리용 worker 프로세스 생성
if( MakeWorkerAccess() == false )
{
LOG( LERR, "[Main %d] content access time extract worker process create failed. => Process exit.", getpid() );
ReadyToExit();
return EXIT_FAILURE;
}
// Worker 생성되면.. worker 초기화 작업을 위해 약 2 sec 정도 대기
sleep( 2 );
// Process Rename..
set_ps_display( PROG_NAME": Main", false );
// Main Process : 그냥 대기
while( 1 )
{
// Main Process 는 할 일이 없다.
// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
// => 따라서 그냥 시그널 대기
pause();
}
// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.
ReadyToExit();
return EXIT_SUCCESS;
}
// 사용방법 표시
void PrintUsage()
{
fprintf( stderr, "\n" );
fprintf( stderr, "Usage: " PROG_NAME " [-h] [-v] [-c {file}] \n" );
fprintf( stderr, "Options: \n" );
fprintf( stderr, " -h Display help information \n" );
fprintf( stderr, " -v Display version \n" );
fprintf( stderr, " -c {file} Use {file} as config file \n" );
fprintf( stderr, "\n" );
fprintf( stderr, PROG_NAME " is Solbox Cloud Storage module.\n" );
fprintf( stderr, " - content access time extract module.\n" );
fprintf( stderr, " -- content access time extract and save to DB.\n\n" );
return;
}
// 버전 정보 표시
void PrintVersion()
{
fprintf( stderr, "\n" );
fprintf( stderr, PROG_NAME " version: " PROG_VERSION "\n\n" );
return;
}
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
bool IsCurrentProcessRun()
{
char tempBuffer[512];
FILE * fd = NULL;
bool bRun = false;
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x %s | sort", PROG_NAME );
fd = popen( tempBuffer, "r" );
if( fd == NULL )
{
std::cerr << "[error] Process duplication check failed.[popen error][" << strerror( errno ) << "]" << std::endl;
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
return true;
}
else
{
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
{
std::string tempPid( tempBuffer );
Trim( tempPid );
if( atoi( tempPid.c_str() ) != getpid() )
{
std::cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << std::endl;
bRun = true;
break;
}
}
pclose( fd );
return bRun;
}
}
/// @brief std 상에 trim 함수가 없어서 직접 구현. 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
/// @return void
void Trim( std::string & str )
{
if( str.length() == 0 )
return;
// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
std::string::size_type pos = str.find_last_not_of( " \a\b\f\n\r\t\v" );
if( pos != std::string::npos )
str.erase( pos + 1 );
// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
pos = str.find_first_not_of( " \a\b\f\n\r\t\v" );
if( pos != std::string::npos )
str.erase( 0, pos );
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExit()
{
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker 프로세스 종료 신호 수신시
static void SignalWorkerDead( int nSignalNumber )
{
pid_t deadPid;
int nDeadStatus;
while( ( deadPid = waitpid( -1, &nDeadStatus, WNOHANG ) ) > 0 )
{
// Worker 프로세스가 signal 에 의해 종료되었는지 검사.
// - 종료 사유 로깅 및
// - Worker 의 초기화 실패로 인한 종료시... Main 프로세스가 종료되도록 처리.
if( WIFSIGNALED( nDeadStatus ) )
{
// signal 에 의해 종료된 경우...
// - 관련 내역 로깅
if( deadPid == g_pidWorkerAccess )
{
_LOG( LWAR, "[Main %d] content access time extract worker process[%d] killed by signal[%d]"
, getpid(), deadPid, WTERMSIG( nDeadStatus ) );
}
else
{
_LOG( LWAR, "[Main %d] Unknown child process[%d] killed by signal[%d]"
, getpid(), deadPid, WTERMSIG( nDeadStatus ) );
}
}
else
{
// signal 에 종료된 상황이 아닌 경우...
// - 각 Worker 의 초기화 실패 발생시... return 구문에 의해 종료됨.
// - 각 Worker 가 signal 을 받아서.. 정상적으로 return 처리하여 종료된 경우.
// - 기타 등등
// 관련 내역 로깅.
if( deadPid == g_pidWorkerAccess )
{
_LOG( LWAR, "[Main %d] content access time extract worker process[%d] exited.", getpid(), deadPid );
}
else
{
_LOG( LWAR, "[Main %d] Unknown child process[%d] exited.", getpid(), deadPid );
}
// 만약 Worker 프로세스들이 초기화 실패로 인해 EXIT_FAILURE 반환되는 상황이라면...
// 자식 프로세스를 재생성하지 않고.. 프로세스를 종료 처리한다.
if( WIFEXITED( nDeadStatus ) )
{
if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
{
if( deadPid == g_pidWorkerAccess )
{
_LOG( LWAR, "[Main %d] content access time extract worker process[%d] initialize failed.", getpid(), deadPid );
}
else
{
_LOG( LWAR, "[Main %d] Unknown child process[%d] exit with fail.", getpid(), deadPid );
}
_LOG( LERR, "[Main %d] Program abnormally exited because Worker initialize faild.", getpid() );
// Main 종료 시그널 발생
raise( SIGTERM );
continue;
}
}
}
// 위에서 로깅 및 초기화 실패로 인한 Main 종료 처리 작업을 수행
// 그 외의 경우에는 다음과 같이 한다.
// - 심각한 문제가 발생한 경우 일 경우에는... 전체 프로세스를 종료 처리한다.
// - 그 외에는 Worker 를 재생성 처리한다.
if( deadPid == g_pidWorkerAccess )
{
if( MakeWorkerAccess() == false )
{
_LOG( LERR, "[Main %d] content access time extract worker process recreate failed.=> Main process exit.", getpid() );
raise( SIGTERM );
}
else
{
_LOG( LWAR, "[Main %d] content access time extract worker process recreate success.", getpid() );
}
}
}
// 오류 발생시 해당 내역 로깅
if( deadPid < 0 )
{
int errorNum = errno;
LOG( LERR, "[Main %d] SIG_CHLD receive but waitpid return error[%d][%s]"
, getpid(), errorNum, strerror( errorNum ) );
}
return;
}
// Main 프로세스에 대한 종료 처리 수신시
static void SignalMainTerminate( int nSignalNumber )
{
// 자식 프로세스를 종료 처리한다.
if( g_pidWorkerAccess != -1 )
{
kill( g_pidWorkerAccess, SIGTERM );
}
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[Main %d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[Main %d] Process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber );
}
// 잠시 대기
sleep( 1 );
// 기타 모듈들의 종료 처리 수행
ReadyToExit();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Main Process 의 signal 처리기...
// fork 된 worker 프로세스 역시 기본적으로 본 signal 처리 action 을 상속받는다.
void SetSignalMain()
{
sigset_t set;
struct sigaction act;
memset( &act, 0x00, sizeof( act ) );
sigfillset( &set );
sigprocmask( SIG_SETMASK, &set, NULL );
sigfillset( &act.sa_mask );
// 무시 처리 signal
act.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &act, NULL ); /* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
sigaction( SIGHUP, &act, NULL ); /* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
sigaction( SIGINT, &act, NULL ); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
sigaction( SIGQUIT, &act, NULL ); /* 키보드에 의한 Abort 신호 처리 => ? */
// Worker child process 종료에 대한 처리기 설정.
act.sa_handler = SignalWorkerDead;
sigaction( SIGCHLD, &act, NULL );
// 사용자의 종료 또는 에러 관련 신호 처리.
act.sa_handler = SignalMainTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
}
// content access time extract worker 프로세스 생성.
bool MakeWorkerAccess()
{
// fork() 결과를 Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장.
g_pidWorkerAccess = fork();
if( g_pidWorkerAccess == 0 )
{
// 생성된 Worker 프로세스인 경우...
int iResult;
// content access time extract worker 프로세스의 Main 함수 호출
iResult = WorkerAccessMain();
// content access time extract worker 프로세스의 Main 함수 종료시 종료 처리
_LOG( LINF, "[Worker %d] content access time extract worker process exit. return[%d]", getpid(), iResult );
ReadyToExit();
exit( iResult );
}
else if( g_pidWorkerAccess < 0 )
{
// fork 실패시
int errorNum = errno;
LOG( LERR, "[Main %d] content access time extract worker process create failed. [%d][%s]"
, getpid(), errorNum, strerror( errorNum ) );
return false;
}
else
{
// 현재의 Main 프로세스
_LOG( LINF, "[Main %d] content access time extract worker process created.. PID[%d]", getpid(), g_pidWorkerAccess );
// 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
+66
View File
@@ -0,0 +1,66 @@
/****************************************************************************
rc_accessd header
-----------------------------------------
begin : 2021/04/05
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __RC_ACCESSD_MAIN_H__
#define __RC_ACCESSD_MAIN_H__
#include <unistd.h>
#include <stdlib.h>
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
/// @brief 사용 방법을 화면에 표시하기 위한 함수.
void PrintUsage( void );
/// @brief Version 정보를 화면에 표시하기 위한 함수
void PrintVersion( void );
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
bool IsCurrentProcessRun( void );
/// @brief std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
/// @param str [in/out] Trim 할 문자열을 저장한 String 참조변수.
/// @return void
void Trim( std::string & str );
/// @brief 프로세스 종료에 따른 반복적인 종료 관련 작업을 수행하기 위한 함수.
void ReadyToExit( void );
/// @brief Main 프로세스 signal 처리 설정을 위한 함수
/// @return void
void SetSignalMain( void );
/// @brief content access 정보 추출을 위한 worker 프로세스 생성.
/// @return 생성 성공시 true, 실패시 false
bool MakeWorkerAccess();
#ifdef __cplusplus
}
#endif
#endif /* __RC_ACCESSD_MAIN_H__ */
+81
View File
@@ -0,0 +1,81 @@
#****************************************************************************
# Makefile for rc_accessd
# -----------------------------------------
#
# begin : 2021/04/05
# copyright : (C) 2005 Solbox Inc.
# author : Dev Storage Team
# email : huibong@solbox.com
# version : 3.5
#
# CopyRight(C) 2005 Solbox Inc. All Rights reserved.
# Redistribution and use in source and binary forms, with or with out
# modification, are not permitted in outside of Solbox Inc.
#****************************************************************************
# Program info
PROG_NAME = rc_accessd
REVISION = 1536
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
DEFAULT_CONFIG_FILE = /user/service/etc/rc_accessd.conf
INSTALL_BIN = /user/service/bin
INSTALL_CONF = /user/service/etc
# Compiler info
CC = /usr/bin/g++
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized \
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual \
-fno-rtti -D_REENTRANT -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
LFLAGS =
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"
# Application Enviroment
APP = $(PROG_NAME)
DIR_INCLUDE = -I../lib -I../ftsd -I/user/db/pgsql/include
DIR_LIB = -L../lib
OBJ = ProcessConfig.o Database.o RcdbInfo.o \
DataAccess.o ThreadTargetSelect.o ../ftsd/FtsdSocketControl.o \
ThreadExtractControl.o ThreadAccessTime.o ThreadDbUpdate.o \
WorkerAccess.o \
Main.o
LIBS = ../lib/libInterCommon.a -lpthread -lrt /user/db/pgsql/lib/libpq.a
#---------------------------------------------------------------------#
all:$(APP)
sync
%.o: %.cpp
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
$(PROG_NAME): $(OBJ)
$(CC) $(LFLAGS) -o $@ $^ $(DFLAGS) $(DIR_LIB) $(LIBS)
clean:
-rm -f *.o core.* *.out *.log
-rm -f ../ftsd/*.o ../ftsd/*.out ../ftsd/core.*
-rm -f $(APP)
sync
install : $(APP)
-cp $(PROG_NAME) $(INSTALL_BIN)/$(PROG_NAME)
sync
# End of Makefile
+280
View File
@@ -0,0 +1,280 @@
#include "ProcessConfig.h"
#include <stdlib.h>
#include <string.h>
#include "Logger.h"
// static 변수 초기화
CProcessConfig * CProcessConfig::m_pInstance = NULL;
// 생성자
CProcessConfig::CProcessConfig()
{
// default 값 설정.
// FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
m_nFhsTransferDaemonPort = 14001;
m_nMaxQueueSize = 4096;
m_nMaxThreadCount = 10;
}
// 소멸자
CProcessConfig::~CProcessConfig()
{
// 객체 소멸시...
// 만약 static GetInstance() 함수가 가르키는 객체가 자기 자신이라면...
// 소멸 처리에 의해 문제가 발생할 수 있으므로... 이에 대한 처리를 해 준다.
if( CProcessConfig::GetInstance() == this )
{
CProcessConfig::m_pInstance = NULL;
}
}
bool CProcessConfig::Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
{
// 1. 임시 처리용 Instance 를 생성한다.
CProcessConfig * pTempInstance = new CProcessConfig();
// 2. 전달 받은 Config 정보를 임시 Instance 로 로딩한다.
// - 만약 로딩이 실패할 경우... 임시 Instance 객체를 삭제 처리한다.
if( pTempInstance->Load( strProgramName, strConfFileName, strErrorMessage ) == false )
{
delete pTempInstance;
pTempInstance = NULL;
return false; // 생성 실패 사유는 Error String 관련 함수를 사용하여 확인.
}
// 3. 전달받은 Conf 정보 로딩에 성공한 경우...
// - 기존 Instance 객체가 존재하는 경우.. 교체 처리..
// - 기존 Instance 객체가 없는 경우는 신규 Instance 사용토록 처리.
if( CProcessConfig::GetInstance() == NULL )
{
CProcessConfig::m_pInstance = pTempInstance;
}
else
{
// Instance replace...
CProcessConfig * pPreviosInstance = CProcessConfig::m_pInstance;
CProcessConfig::m_pInstance = pTempInstance;
delete pPreviosInstance;
}
return true;
}
CProcessConfig * CProcessConfig::GetInstance()
{
return CProcessConfig::m_pInstance;
}
bool CProcessConfig::Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
{
// 변수 유효성 확인
if( strProgramName.size() <= 0 )
{
strErrorMessage = "Config Program Name[" + strProgramName + "] not valid.";
return false;
}
// Config class (lib 하위) 를 이용하여 config 파일 상의 정보를 로딩 처리
Config conf;
if( conf.Open( strConfFileName ) == false )
{
strErrorMessage = "Config file[" + strConfFileName + "] open failed.";
return false;
}
// Config 모듈에 로딩된 정보 중 필요한 정보만 가져온다...
// - 만약 오류가 발생할 경우... 해당 내역은 Error String 에 저장 처리...
std::string strValue;
//std::vector< std::string > vecValue;
// DEFAULT_LOG_DIR
if( GetConfigValue( conf, strProgramName, "DEFAULT_LOG_DIR", strValue, strErrorMessage ) == false )
return false;
else
{
m_strLogPath = strValue;
strValue.clear();
}
// LOG_LEVEL
if( GetConfigValue( conf, strProgramName, "LOG_LEVEL", strValue, strErrorMessage ) == false )
return false;
else
{
m_nLogLevel = atoi(strValue.c_str());
if( m_nLogLevel < 0 || m_nLogLevel > MAX_LOG_LEVEL )
{
strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// RCDB IP
if( GetConfigValue( conf, strProgramName, "RCDB_IP", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbIp = strValue;
strValue.clear();
}
// RCDB PORT
if( GetConfigValue( conf, strProgramName, "RCDB_PORT", strValue, strErrorMessage ) == false )
return false;
else
{
m_nRcdbPort = atoi( strValue.c_str() );
if( m_nRcdbPort <= 0 || m_nRcdbPort > 65535 )
{
strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// RCDB DB NAME
if( GetConfigValue( conf, strProgramName, "RCDB_DB_NAME", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbName = strValue;
strValue.clear();
}
// RCDB ACCOUNT
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbAcct = strValue;
strValue.clear();
}
// RCDB ACCOUNT PASSWORD
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT_PW", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbAcctPw = strValue;
strValue.clear();
}
// FHS_TRANSFER_DAEMON_PORT
// - 설정이 없는 경우 default 값 사용
if( GetConfigValue(conf, strProgramName, "FHS_TRANSFER_DAEMON_PORT", strValue, strErrorMessage) == true )
{
m_nFhsTransferDaemonPort = atoi(strValue.c_str());
if( m_nFhsTransferDaemonPort <= 0 || m_nFhsTransferDaemonPort > 65535 )
{
strErrorMessage = "Config [FHS_TRANSFER_DAEMON_PORT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// MAX_QUEUE_COUNT
// - 설정이 없는 경우 default 값 사용
if( GetConfigValue(conf, strProgramName, "MAX_QUEUE_COUNT", strValue, strErrorMessage) == true )
{
m_nMaxQueueSize = atoi(strValue.c_str());
// queue 의 최소 크기는 2048
if( m_nMaxQueueSize < 2048 )
{
strErrorMessage = "Config [MAX_QUEUE_COUNT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// MAX_THREAD_COUNT
// - 설정이 없는 경우 default 값 사용
if( GetConfigValue(conf, strProgramName, "MAX_THREAD_COUNT", strValue, strErrorMessage) == true )
{
m_nMaxThreadCount = atoi(strValue.c_str());
if( m_nMaxThreadCount <= 0 )
{
strErrorMessage = "Config [MAX_THREAD_COUNT] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// ACCESS_TIME_EXTRACT_TARGET_SERVICE_SEQ
// - 설정이 없는 경우 오류 처리
if( GetConfigValue( conf, strProgramName, "ACCESS_TIME_EXTRACT_TARGET_SERVICE_SEQ", strValue, strErrorMessage ) == false )
{
return false;
}
else
{
m_nAccessTimeExtractService = atoi(strValue.c_str());
if( m_nAccessTimeExtractService <= 0 )
{
strErrorMessage = "Config [ACCESS_TIME_EXTRACT_TARGET_SERVICE_SEQ] value[" + strValue + "] is not valid";
return false;
}
strValue.clear();
}
// conf 정보 로딩이 모두 정상적으로 완료된 경우... 마지막으로 전달받은 config 명, section 정보를 저장한다.
m_strConfigFileName = strConfFileName;
m_strProgramName = strProgramName;
return true;
}
bool CProcessConfig::GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage )
{
// conf get value
if( conf.GetConfig( strProgramName, strKey, strValue) == false )
{
if( conf.GetConfig( "COMMON", strKey, strValue) == false )
{
strErrorMessage = "Config [" + strKey + "] value is not exist";
return false;
}
}
// value check.
if( strValue.empty() == true )
{
strErrorMessage = "Config [" + strKey + "] value is not valid";
return false;
}
return true;
}
+122
View File
@@ -0,0 +1,122 @@
/****************************************************************************
rc_accessd config processing module
-----------------------------------------
begin : 2021/04/06
copyright : (C) 2005 Solbox Inc.
author : Dev Storage team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __PROCESS_CONFIG_H__
#define __PROCESS_CONFIG_H__
#include <string>
#include "Config.h"
// 본 모듈은 conf 파일의 정보를 read 하여... 본 프로세스 관련 설정 정보를 저장한다.
// 본 모듈은 singleton 으로 동작시켜 Main 프로세스 및 Worker 프로세스에서도 접근이 가능토록 한다.
// conf 파일에 대한 파싱 및 설정 정보 추출은 lib 하위의 Config 클래스를 이용한다.
// 본 객체는 singleton 객체이므로 상속받아 사용하지 않도록 한다. ( 상속받아 사용할 수 없도록 private 처리함 )
class CProcessConfig
{
private:
// sigleton 객체 생성을 위해 생성자를 public 으로 처리하지 않음.
CProcessConfig();
// 소멸자
virtual ~CProcessConfig();
static CProcessConfig * m_pInstance;
public:
// 객체 초기화 처리용 Function.
// 본 함수 호출시 Instance 생성 후 입력 받은 Config 파일 load.
// 따라서 본 함수를 호출하지 않을 경우.. GetInstance() 함수는 NULL 을 반환
// 본 함수를 객체가 생성된 상태에서 재 호출할 경우
// - 주어진 Conf 파일 정보가 정확하다면.. conf 정보를 다시 load 함.
// - 주어진 conf 파일 정보가 부정확하다면... 이전 conf 를 계속 사용함.
static bool Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
// Singleton 객체 접근 메소드
static CProcessConfig * GetInstance();
private:
// 전달받은 conf 파일에서 해당 config 정보를 로드하여 멤버 변수에 저장한다.
// 만약 반환값이 false 인 경우 해당 오류 관련 내역은 strErrorMessage 변수에 저장된다.
bool Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
// Config 객체로 부터 지정된 strKey 정보를 가져와 유효성 여부를 판단.
// 반환값이 false 인 경우 strErrorMessage 상에 오류 내역이 저장된다.
bool GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage );
private:
// 객체 초기화시 전달받은 정보
std::string m_strConfigFileName; // 설정 파일 정보
std::string m_strProgramName; // 프로그램 명 (conf 상에서 해당 프로그램의 설정 정보를 가져올 때 사용)
// 기본 설정 정보.
std::string m_strLogPath; // 로그 저장 Path (/user/service/logs)
int m_nLogLevel; // 로그 기록 Level
std::string m_strRcdbIp; // RCDB IP
unsigned int m_nRcdbPort; // RCDB Port
std::string m_strRcdbName; // RCDB DB Name
std::string m_strRcdbAcct; // RCDB 접근 계정
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
// 프로세스 설정 정보
unsigned int m_nFhsTransferDaemonPort; // FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
unsigned int m_nMaxQueueSize; // Max Queue Size ( default: 4096 )
unsigned int m_nMaxThreadCount; // Max Thread Count ( default: 10 )
unsigned int m_nAccessTimeExtractService; // Access time extract target service sequence
public: // 멤버 변수에 대한 Get 함수 선언 및 정의
const char * GetConfigFileName() { return m_strConfigFileName.c_str(); }
const char * GetProgramName() { return m_strProgramName.c_str(); }
const char * GetLogPath() { return m_strLogPath.c_str(); }
int GetLogLevel() { return m_nLogLevel; }
const char * GetRcdbIp() { return m_strRcdbIp.c_str(); }
unsigned int GetRcdbPort() { return m_nRcdbPort; }
const char * GetRcdbName() { return m_strRcdbName.c_str(); }
const char * GetRcdbAcct() { return m_strRcdbAcct.c_str(); }
const char * GetRcdbAcctPw() { return m_strRcdbAcctPw.c_str(); }
unsigned int GetFhsTransferDaemonPort() { return m_nFhsTransferDaemonPort; }
unsigned int GetMaxQueueSize() { return m_nMaxQueueSize; }
unsigned int GetMaxThreadCount() { return m_nMaxThreadCount; }
unsigned int GetAccessTimeExtractService() { return m_nAccessTimeExtractService; }
};
#endif /* __PROCESS_CONFIG_H__ */
+40
View File
@@ -0,0 +1,40 @@
#include "RcdbInfo.h"
#include "ProcessConfig.h"
// 생성자..
CRcdbInfo::CRcdbInfo()
{
}
// 소멸자..
CRcdbInfo::~CRcdbInfo()
{
}
// CServiceConfig Class 에 저장된 RCDB 접속 정보를 가져와 멤버 변수에 저장한다.
void CRcdbInfo::Load()
{
// CProcessConfig Class 에서 기본적인 설정값에 대해 검사를 수행하므로...
// 본 Class 에서 다시 유효성 검사를 할 필요는 없다.. ( 어차피 유효성 검사 방법이 동일하므로.. )
// RCDB IP
m_strRcdbIp = CProcessConfig::GetInstance()->GetRcdbIp();
// RCDB Port
m_nRcdbPort = CProcessConfig::GetInstance()->GetRcdbPort();
// RCDB DB Name
m_strRcdbName = CProcessConfig::GetInstance()->GetRcdbName();
// RCDB 접근 계정
m_strRcdbAcct = CProcessConfig::GetInstance()->GetRcdbAcct();
// RCDB 접근 Password.
m_strRcdbAcctPw = CProcessConfig::GetInstance()->GetRcdbAcctPw();
return;
}
+52
View File
@@ -0,0 +1,52 @@
/****************************************************************************
RCDB 접속 정보 저장 처리 Class
-----------------------------------------
begin : 2013/11/13
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __RCDB_INFO_H__
#define __RCDB_INFO_H__
#include <unistd.h>
#include <string>
/// @brief CRcdbInfo
/// 본 클래스는 conf 파일에 저장된 RCDB 접속 관련 정보를 저장하기 위한 공용 Class...
/// 본 클래스를 사용하지 않고... CServiceConfig class 의 Get 함수를 이용하여 RCDB 접속 정보를 처리해도 된다..
/// 하지만... RCDB 접속 처리 Class 함수의 string type 변수 사용으로 인해... 여러 모듈에서 반복적인 동일 코드 처리 작업을 수행해야 하므로...
/// 본 Class 를 통해 코드를 단순화 시킬 목적으로 사용한다.
class CRcdbInfo
{
public:
// 생성 및 소멸자.
CRcdbInfo();
~CRcdbInfo();
// CServiceConfig class 에 저장된 RCDB 접속 정보를 멤버 변수에 저장처리한다.
void Load( void );
public:
// 멤버 변수들은 DB 접속을 처리하는 객체에서 직접 Access 해서 사용할 수 있도록 public 으로 구성. ( 편의성 제공 목적)
std::string m_strRcdbIp; // RCDB IP
unsigned int m_nRcdbPort; // RCDB Port
std::string m_strRcdbName; // RCDB DB Name
std::string m_strRcdbAcct; // RCDB 접근 계정
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
};
#endif /* __RCDB_INFO_H__ */
+265
View File
@@ -0,0 +1,265 @@
#include "ThreadAccessTime.h"
#include "../ftsd/FtsdSocketControl.h"
#include "ProcessConfig.h"
#include <errno.h>
#include <string.h>
#include <sys/syscall.h>
CThreadAccessTime::CThreadAccessTime( CDataAccess & data, CDataQueue<CDataAccess> * const pQueueComplete
, CThreadExtractControl * const pControl )
: m_data(data), m_pQueueComplete(pQueueComplete), m_pControl(pControl)
{
// 멤버 변수 초기화
m_threadHandle = 0;
}
CThreadAccessTime::~CThreadAccessTime()
{
// 내부 therad 가 동작 중인 경우... 중지 처리
if( m_threadHandle != 0 )
{
pthread_cancel( m_threadHandle );
}
m_pQueueComplete = NULL;
m_pControl = NULL;
}
bool CThreadAccessTime::Start( void )
{
// 유효성 check
if( m_pQueueComplete == NULL )
{
LOG( LERR, "CThreadAccessTime: queue object pointer not valid." );
return false;
}
if( m_pControl == NULL )
{
LOG( LERR, "CThreadAccessTime: control object pointer not valid." );
return false;
}
// 작업 content 에 대한 유효성 check
if( m_data.m_strHostname.size() < 7 || m_data.m_strFilenamehash.size() < 5 )
{
LOG( LERR, "CThreadAccessTime: content info not valid.[%s][%s]"
, m_data.m_strHostname.c_str(), m_data.m_strFilenamehash.c_str() );
return false;
}
// 작업 thread 생성
int nResult = pthread_create( &m_threadHandle, NULL, CThreadAccessTime::threadFunc, this );
if( nResult != 0 )
{
// thread 생성 실패
int errorNum = errno;
LOG( LERR, "CThreadAccessTime: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
void * CThreadAccessTime::threadFunc( void * arg )
{
CThreadAccessTime * pObject = reinterpret_cast<CThreadAccessTime *>( arg );
pthread_detach( pthread_self() );
pthread_testcancel();
// Thread 동작시 control class 에 count 증가 처리
pObject->m_pControl->PlusThreadCountAccess();
// 실제 작업 수행
pObject->Execute();
// Thread 동작 완료시 control class 에 count 감소 처리
pObject->m_pControl->MinusThreadCountAccess();
pthread_testcancel();
// thread 종료시 handle 정보 초기화
pObject->m_threadHandle = 0;
// 객체 자동 delete 처리
delete pObject;
return NULL;
}
void CThreadAccessTime::Execute( void )
{
// FHS ftsd 와 통신 처리 담당한 socket 객체 생성
CFtsdSocketControl fhs;
// FHS connect
bool bResult = fhs.ConnectTarget( m_data.m_strHostname, CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() );
// FHS 로 access time 정보 추출 요청
bResult = fhs.SendFileStatRequest( m_data.m_strFilenamehash );
if( bResult == false )
{
// content stat 요청 실패
LOG( LERR, "[FHS ] %lu: content stat request send fail to fhs. %s %llu %s "
, GetThreadId()
, m_data.m_strHostname.c_str(), m_data.m_uResourceId , m_data.m_strFilenamehash.c_str() );
return;
}
// FHS 로 부터 결과 수신 대기
// response timeout 처리 관련 변수
struct timespec timeStart, timeNow;
double timeElapsed = 0.0;
memset( &timeStart, 0x00, sizeof( struct timespec ) );
memset( &timeNow, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &timeStart );
// 응답 결과 저장을 위한 변수
int nResult;
bool bSuccess = false;
std::string szErrorMessage;
while( 1 )
{
nResult = fhs.GetFileStatResult( 5, bSuccess, szErrorMessage
, m_data.m_lFileSize, m_data.m_lAccessTime, m_data.m_lModifyTime, m_data.m_lChangeTime );
if( nResult == 0 )
{
// 지정된 시간동안 결과 수신 못한 경우
if( fhs.SendAliveCheck() == false )
{
// 다음 loop 에서 -1 연결 종료 처리되므로.. 여기서는 로깅만..
LOG( LERR, "[FHS ] %lu: content stat alive check fail. %s %llu %s"
, GetThreadId()
, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );
}
else
{
// timeout 발생했으므로.. 2 sec 대기 후 다시 시도
sleep( 2 );
}
}
else if( nResult == 1 )
{
// 처리 시간 계산
clock_gettime( CLOCK_MONOTONIC, &timeNow );
timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;
// FHS 로 부터 응답 수신
if( bSuccess == true )
{
// logging
_LOG( LDBG, "[FHS ] %lu: stat check ok. %.2lf %s %llu %s %ld %ld %ld"
, GetThreadId()
, timeElapsed
, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str()
, m_data.m_lAccessTime, m_data.m_lModifyTime, m_data.m_lChangeTime );
}
else
{
// content 존재하지 않는 경우
// - 존재하지 않는 파일에 대해 DB 상에 -1 로 업데이트 되도록 처리
m_data.m_lAccessTime = -1;
m_data.m_lModifyTime = -1;
m_data.m_lChangeTime = -1;
m_data.m_lFileSize = 0;
_LOG( LWAR, "[FHS ] %lu: stat check error. %.2lf %s %llu %s [%s]"
, GetThreadId()
, timeElapsed
, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str()
, szErrorMessage.c_str() );
}
// complete queue 에 push 처리
if( m_pQueueComplete->Push( m_data ) == false )
{
// 실패시 약 3 sec 대기 후 1회만 재시도
sleep( 3 );
if( m_pQueueComplete->Push( m_data ) == false )
{
LOG( LERR, "[FHS ] %lu: complete queue push fail. %s %llu %s"
, GetThreadId()
, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );
break;
}
}
// 처리 결과 수신에 따른 loop 종료
break;
}
else if( nResult == -1 )
{
// 처리 시간 계산
clock_gettime( CLOCK_MONOTONIC, &timeNow );
timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;
// FHS 와 연결 끝어진 경우...
LOG( LERR, "[FHS ] %lu: stat check fail. fhs connection broken. %.2lf %s %llu %s"
, GetThreadId(), timeElapsed
, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );
break;
}
else if( nResult == 2 )
{
// 대상 응답이 아닌 경우.. 무시
;
}
else
{
// 미정의된 상황
LOG( LERR, "[FHS ] %lu: receive undefine result[%d]. %s %llu %s"
, GetThreadId(), nResult
, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );
break;
}
// Total Reponse 대기 시간 확인
clock_gettime( CLOCK_MONOTONIC, &timeNow );
timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;
if( timeElapsed > 60.0 ) // 1분 이상 대기하는 경우 실패 처리
{
LOG( LERR, "[FHS ] %lu: stat check fail. response wait %.2lf sec, but timeout 60 sec. %s %llu %s"
, GetThreadId(), timeElapsed
, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );
break;
}
} // while(1)
fhs.Close();
}
long CThreadAccessTime::GetThreadId( void )
{
return syscall( SYS_gettid );
}
+74
View File
@@ -0,0 +1,74 @@
/****************************************************************************
rc_accessd content access time extract header
-----------------------------------------
begin : 2021/04/14
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __THREAD_ACCESS_TIME_H__
#define __THREAD_ACCESS_TIME_H__
#include <pthread.h>
#include "DataAccess.h"
#include "DataQueue.hpp"
#include "ThreadExtractControl.h"
// CThreadAccessTime class
// - 전달받은 data 에 대해 FHS ftsd 와 통신을 통해 access time 정보를 추출하여...
// - 전달받은 queue 에 저장 처리 수행
class CThreadAccessTime
{
public:
CThreadAccessTime( CDataAccess & data, CDataQueue<CDataAccess> * const pQueueComplete, CThreadExtractControl * const pControl );
~CThreadAccessTime();
// Thread 생성 및 작업 시작
bool Start( void );
private:
// thread handle
pthread_t m_threadHandle;
// 작업 대상 content 정보 객체
CDataAccess m_data;
// 작업 완료된 content 목록을 저장하기 위한 queue 로서... 생성자를 통해 전달받은 queue point 를 의미
CDataQueue<CDataAccess> * m_pQueueComplete;
// thread 상태를 check 하는 상위 control 객체에 대한 포인터
CThreadExtractControl * m_pControl;
private:
// Thread function
static void * threadFunc( void * arg );
// 실제 작업을 수행하기 위한 함수
void Execute( void );
long GetThreadId( void );
};
#endif /* __THREAD_ACCESS_TIME_H__ */
+265
View File
@@ -0,0 +1,265 @@
#include "ThreadDbUpdate.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include <errno.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
#define SLEEP_TIME_DEFAULT 5
#define SLEEP_TIME_MAX 10
CThreadDbUpdate::CThreadDbUpdate( CDataQueue<CDataAccess> * const pQueue, unsigned int uTargetService )
: m_pQueue( pQueue ), m_uTargetService( uTargetService )
{
// 멤버변수 초기화
m_threadHandle = 0;
m_uSleep = SLEEP_TIME_DEFAULT;
}
CThreadDbUpdate::~CThreadDbUpdate()
{
// 내부 therad 가 동작 중인 경우... 중지 처리
if( m_threadHandle != 0 )
{
pthread_cancel( m_threadHandle );
}
sleep( 1 ); // thread 종료 잠시 대기
// 혹시나 해서 정석대로 내부 변수 NLLL 처리
if( m_pQueue != NULL )
m_pQueue = NULL;
}
bool CThreadDbUpdate::Start( void )
{
// 멤버 변수 유효성 check
if( m_pQueue == NULL )
{
LOG( LERR, "CThreadDbUpdate: content queue pointer is null." );
return false;
}
// RCDB 접속 정보 load
m_rcdbInfo.Load();
// 작업 thread 생성
int nResult = pthread_create( &m_threadHandle, NULL, CThreadDbUpdate::threadFunc, this );
if( nResult != 0 )
{
// thread 생성 실패시
int errorNum = errno;
LOG( LERR, "CThreadDbUpdate: thread create failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////
void * CThreadDbUpdate::threadFunc( void * arg )
{
CThreadDbUpdate * pObject = reinterpret_cast<CThreadDbUpdate *> ( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 지정된 주기로 실행
sleep( pObject->m_uSleep );
}
// thread 종료시
pObject->m_threadHandle = 0;
return NULL;
}
void CThreadDbUpdate::Execute( void )
{
// sleep time 조정 처리
m_uSleep = SLEEP_TIME_DEFAULT;
// 조회 queue 상에 처리할 data 가 존재하는지 확인
unsigned int uStandbyCount = m_pQueue->GetSize();
if( uStandbyCount == 0)
{
m_uSleep = SLEEP_TIME_MAX;
_LOG( LINF, "[UPDATE ] completed queue empty [%u / %u]. max sleep %u sec."
, uStandbyCount, m_pQueue->GetMaxSize(), m_uSleep );
return;
}
_LOG( LINF, "[UPDATE ] start with target service: %u, queue_count: %u"
, m_uTargetService, uStandbyCount );
// DB 처리를 수행할 객체 생성
// DB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
DataBase db;
// DB 와 연결을 시도한다.
if( db.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort, m_rcdbInfo.m_strRcdbName
, m_rcdbInfo.m_strRcdbAcct, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
{
// 연결 실패시...
m_uSleep = SLEEP_TIME_DEFAULT;
LOG( LERR, "[UPDATE ] db connection failed. sleep %u sec.", m_uSleep );
return;
}
// DB 상에 access time 업데이트 작업을 수행한다.
UpdateContentAccessTime( db );
// DB 와의 연결을 명시적으로 해제 처리
db.PgCloseDB();
return;
}
bool CThreadDbUpdate::UpdateContentAccessTime( DataBase & db )
{
// Query 수행 시간 check 를 위한 변수
struct timespec timeStart, timeNow;
double timeElapsed = 0.0;
memset( &timeStart, 0x00, sizeof( struct timespec ) );
memset( &timeNow, 0x00, sizeof( struct timespec ) );
CDataAccess data;
char szQuery[4096];
int nResult = 0;
int nPopCount = 0;
LOG( LINF, "[UPDATE ] access time db update start. standby queue count: %u", m_pQueue->GetSize() );
// start 시간 check.
clock_gettime( CLOCK_MONOTONIC, &timeStart );
// queue 에 존재하면 무조건 처리한다.
while( m_pQueue->GetSize() > 0 )
{
// queue pop
data = m_pQueue->Front();
m_pQueue->Pop();
++nPopCount;
// 대상 content에 대해 access time 정보를 우선 조회
// - max 조회시 대상이 없는 경우 1 row 의 NULL 값을 반환하기 때문에.
// - having count 항목을 추가하여.. max 조회시 대상이 없는 경우.. 0 row 를 반환하도록 처리.
snprintf( szQuery, 4096 - 1
, "SELECT MAX( file_lastaccess ) FROM t_access_%u "
"WHERE uri = '%s' AND resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' "
"HAVING COUNT(*) > 0 ;"
, m_uTargetService
, data.GetQueryURI().c_str()
);
// Query 실행
db.PgDoExec( szQuery );
nResult = db.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// query 수행 실패시
LOG( LERR, "[UPDATE ] content select exec error. [%d][%s][%s]", nResult, db.GetErrorMessage().c_str(), szQuery );
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
db.PgClear();
// 재시도 시간 최대 설정
m_uSleep = SLEEP_TIME_MAX;
// db error 시 loop 종료
break;
}
// 결과 Row 수 확인
nResult = db.GetNoTuples();
if( nResult < 0 )
{
// query 수행 실패시
LOG( LERR, "[UPDATE ] content select tuple error. [%d][%s][%s]", nResult, db.GetErrorMessage().c_str(), szQuery );
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
db.PgClear();
// 재시도 시간 최대 설정
m_uSleep = SLEEP_TIME_MAX;
// db error 시 loop 종료
break;
}
else if( nResult == 0 )
{
// 해당 Content 가 없는 경우..
LOG( LINF, "[UPDATE ] content not found in db. update skip. [%llu %s]", data.m_uResourceId, data.m_strUri.c_str() );
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
db.PgClear();
// db 조회 결과가 없는 경우... 해당 content 는 버린다.
continue;
}
long lPreAccessTime = atol( db.GetValue( 0, 0 ) );
if( lPreAccessTime < data.m_lAccessTime || (lPreAccessTime == 0 && data.m_lAccessTime == -1 ) )
{
// DB access time 시간이 추출된 시간보다 작은 경우 .. DB update 작업을 수행한다.
snprintf( szQuery, 4096 - 1
, "UPDATE t_access_%u "
"SET file_lastaccess = %ld "
"WHERE uri = '%s' AND resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' ;"
, m_uTargetService
, data.m_lAccessTime
, data.GetQueryURI().c_str()
);
// update query 수행
db.PgDoExec( szQuery );
nResult = db.GetCmdTuples();
//_LOG( LDBG, "[UPDATE ] access time update [%ld -> %ld] [%s]", lPreAccessTime, data.m_lAccessTime, data.m_strUri.c_str() );
}
db.PgClear();
} // while()
// 작업 소요 시간 check.
clock_gettime( CLOCK_MONOTONIC, &timeNow );
timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;
LOG( LINF, "[UPDATE ] access time db update end %.2lf sec. queue pop count: %d", timeElapsed, nPopCount );
return true;
}
+78
View File
@@ -0,0 +1,78 @@
/****************************************************************************
rc_accessd db update thread header
-----------------------------------------
begin : 2021/04/15
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __THREAD_DB_UPDATE_H__
#define __THREAD_DB_UPDATE_H__
#include <pthread.h>
#include "DataAccess.h"
#include "DataQueue.hpp"
#include "Database.h"
#include "RcdbInfo.h"
// CThreadDbUpdate class
// - content 에 대한 access time 추출 정보를 DB 에 최종 업데이트 처리 수행
// - update 는 uri 단위로 수행하며.. 기존 db 상에 data 가 존재하는 경우...
// - 추출된 time 정보와 db 정보를 비교하여.. 최신 time 정보만 업데이트 처리함.
class CThreadDbUpdate
{
public:
CThreadDbUpdate( CDataQueue<CDataAccess> * const pQueue, unsigned int uTargetService );
~CThreadDbUpdate();
// theread 생성하여 내부 job 을 주기적으로 수행 처리한다.
bool Start( void );
private:
// thread handle
pthread_t m_threadHandle;
// thread loop 마다 sleep time 를 저장하기 위한 변수
unsigned int m_uSleep;
// 추출된 content 목록이 저장된 queue 로서... 생성자를 통해 전달받은 queue point 를 의미
CDataQueue<CDataAccess> * m_pQueue;
// DB 연결 정보 관리 객체
CRcdbInfo m_rcdbInfo;
// 추출 대상 서비스 정보
unsigned int m_uTargetService;
private:
// thread loop base function
static void * threadFunc( void * arg );
// 실제 content 조회 처리를 담당하는 함수
void Execute( void );
// DB 상에 access time 정보 update 작업을 수행한다.
bool UpdateContentAccessTime( DataBase & db );
};
#endif /* __THREAD_DB_UPDATE_H__ */
@@ -0,0 +1,239 @@
#include "ThreadExtractControl.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include "ThreadAccessTime.h"
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
#define SLEEP_TIME_DEFAULT 0
#define SLEEP_TIME_MAX 10
CThreadExtractControl::CThreadExtractControl( CDataQueue<CDataAccess> * const pQueueTarget
, CDataQueue<CDataAccess> * const pQueueComplete, unsigned int nMaxThreadCount )
: m_pQueueTarget(pQueueTarget), m_pQueueComplete(pQueueComplete), m_nMaxThreadCount(nMaxThreadCount)
{
// 멤버변수 초기화
m_threadHandle = 0;
m_uSleep = SLEEP_TIME_DEFAULT;
m_nCurrentThreadCount = 0;
pthread_mutex_init( &m_mutexThreadCount, NULL );
}
CThreadExtractControl::~CThreadExtractControl()
{
// 내부 therad 가 동작 중인 경우... 중지 처리
if( m_threadHandle != 0 )
{
pthread_cancel( m_threadHandle );
}
sleep( 1 ); // thread 종료 잠시 대기
// 혹시나 해서 정석대로 내부 변수 NLLL 처리
if( m_pQueueTarget != NULL )
m_pQueueTarget = NULL;
if( m_pQueueComplete != NULL )
m_pQueueComplete = NULL;
pthread_mutex_destroy( &m_mutexThreadCount );
}
bool CThreadExtractControl::Start( void )
{
// 유효성 check
if( m_pQueueTarget == NULL || m_pQueueComplete == NULL )
{
LOG( LERR, "CThreadExtractControl: queue object pointer not valid." );
return false;
}
// 작업 thread 생성
int nResult = pthread_create( &m_threadHandle, NULL, CThreadExtractControl::threadFunc, this );
if( nResult != 0 )
{
// thread 생성 실패
int errorNum = errno;
LOG( LERR, "CThreadExtractControl: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
void CThreadExtractControl::PlusThreadCountAccess( void )
{
pthread_mutex_lock( &m_mutexThreadCount );
++m_nCurrentThreadCount;
pthread_mutex_unlock( &m_mutexThreadCount );
}
void CThreadExtractControl::MinusThreadCountAccess( void )
{
pthread_mutex_lock( &m_mutexThreadCount );
--m_nCurrentThreadCount;
pthread_mutex_unlock( &m_mutexThreadCount );
}
int CThreadExtractControl::GetThreadCountAccess( void )
{
return m_nCurrentThreadCount;
}
int CThreadExtractControl::GetCreateableThreadCount( void )
{
int nCreateableCount = 0;
// lock 설정 후 추가 생성 가능한 Copy thread 계산한다.
pthread_mutex_lock( &m_mutexThreadCount );
nCreateableCount = ((int) m_nMaxThreadCount) - m_nCurrentThreadCount;
pthread_mutex_unlock( &m_mutexThreadCount );
// 값 보정
if( nCreateableCount < 0 )
nCreateableCount = 0;
return nCreateableCount;
}
///////////////////////////////////////////////////////////////////////////
void * CThreadExtractControl::threadFunc( void * arg )
{
CThreadExtractControl * pObject = reinterpret_cast<CThreadExtractControl *>( arg );
pthread_detach( pthread_self() );
struct timespec sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_nsec = 300000000; // 0.3 sec
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 지정된 주기로 실행
if( pObject->m_uSleep == 0 )
{
nanosleep( &sleepTime, NULL );
}
else
{
sleep( pObject->m_uSleep );
}
}
// thread 종료시 handle 정보 초기화
pObject->m_threadHandle = 0;
return NULL;
}
void CThreadExtractControl::Execute( void )
{
// sleep time 조정 처리
m_uSleep = SLEEP_TIME_DEFAULT;
// 작업 대상 content 가 존재하는지?
if( m_pQueueTarget->GetSize() == 0 )
{
// MAX sleep
m_uSleep = SLEEP_TIME_MAX;
_LOG( LINF, "[CONTROL] content queue empty. max sleep %u sec.", m_uSleep );
return;
}
// 작업 결과를 저장할 queue 공간이 있는지?
if( m_pQueueComplete->GetFreeSize() < (unsigned int)m_nMaxThreadCount )
{
// MAX sleep
m_uSleep = SLEEP_TIME_MAX;
_LOG( LINF, "[CONTROL] content complte queue full [free %u/%u]. max sleep %u sec."
, m_pQueueComplete->GetFreeSize(), m_pQueueComplete->GetMaxSize(), m_uSleep );
return;
}
// 생성 가능 thread 가 존재하는가?
int nCreateableCount = GetCreateableThreadCount();
if( nCreateableCount == 0 )
{
m_uSleep = SLEEP_TIME_DEFAULT;
_LOG( LINF, "[CONTROL] access time thread all working [%d / %d]. sleep %u sec."
, nCreateableCount, m_nMaxThreadCount, m_uSleep );
return;
}
else
{
_LOG( LINF, "[CONTROL] access time thread createable count [%d / %d]"
, nCreateableCount, m_nMaxThreadCount );
}
// queue 에서 pop 처리하여 처리 thread 를 생성 처리한다.
CDataAccess data;
while( m_pQueueTarget->GetSize() > 0 )
{
// queue pop
data = m_pQueueTarget->Front();
m_pQueueTarget->Pop();
// access time 추출 처리 thrad 생성 처리
CThreadAccessTime * pThreadAccess = new CThreadAccessTime( data, m_pQueueComplete, this );
// Thread 생성 또는 start 처리 실패시
// - thread 생성이 실패할 가능성은 거의 없고..
// - 아마 thread 객체의 Start 처리시 인자 check 등에서 오류일 가능성이 높으므로..
// - 재시도 하지 않고.. 그냥 skip 처리한다.
if( pThreadAccess == NULL || pThreadAccess->Start() == false )
{
// thread 정리
if( pThreadAccess != NULL )
{
delete pThreadAccess;
pThreadAccess = NULL;
}
// 실패 내역 로깅
LOG( LWAR, "[CONTROL] access time thread create or start fail. [%s %llu %s]"
, data.m_strHostname.c_str(), data.m_uResourceId, data.m_strFilenamehash.c_str() );
}
else
{
// thread 가 정상 기동된 경우...
--nCreateableCount;
}
// 생성 가능한 thread 를 전부 생성했는지 check.
if( nCreateableCount <= 0 )
break;
}
return;
}
@@ -0,0 +1,88 @@
/****************************************************************************
rc_accessd content access time extract control header
-----------------------------------------
begin : 2021/04/14
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __THREAD_EXTRACT_CONTROL_H__
#define __THREAD_EXTRACT_CONTROL_H__
#include <pthread.h>
#include "DataAccess.h"
#include "DataQueue.hpp"
// CThreadExtractControl class
// - queue 에 저장된 각 content 에 대해 access time 정보 추출을 처리할 개별 thread 생성 처리 담당.
class CThreadExtractControl
{
public:
CThreadExtractControl( CDataQueue<CDataAccess> * const pQueueTarget
, CDataQueue<CDataAccess> * const pQueueComplete, unsigned int nMaxThreadCount );
~CThreadExtractControl();
// Thread 생성 및 작업 시작
bool Start( void );
// current thread count info
void PlusThreadCountAccess( void );
void MinusThreadCountAccess( void );
int GetThreadCountAccess( void );
int GetCreateableThreadCount( void );
private:
// thread handle
pthread_t m_threadHandle;
// Thread loop 마다 sleep time 저장
unsigned int m_uSleep;
// 작업 대상 content 목록을 저장하기 위한 queue 로서... 생성자를 통해 전달받은 queue point 를 의미
CDataQueue<CDataAccess> * m_pQueueTarget;
// 작업 완료된 content 목록을 저장하기 위한 queue 로서... 생성자를 통해 전달받은 queue point 를 의미
CDataQueue<CDataAccess> * m_pQueueComplete;
// 최대 생성 가능 theread count
unsigned int m_nMaxThreadCount;
// 작업 thread 관리
int m_nCurrentThreadCount;
pthread_mutex_t m_mutexThreadCount;
private:
// Thread function
static void * threadFunc( void * arg );
// 실제 작업을 수행하기 위한 함수
void Execute( void );
};
#endif /* __THREAD_EXTRACT_CONTROL_H__ */
+383
View File
@@ -0,0 +1,383 @@
#include "ThreadTargetSelect.h"
#include "Logger.h"
#include "ProcessConfig.h"
#include <errno.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
#define SLEEP_TIME_DEFAULT 5
#define SLEEP_TIME_MAX 10
// DB 조회시 SELECT limit count
// - queue 의 최소 크기가 2048 이므르.. DB 조회시 2000 씩만 조회하도록 한다.
#define DB_SELECT_LIMIT 2000
// Content select query 수행 시간이 지정된 시간 이상일 경우 로깅 처리 (sec)
#define LOGGING_MAX_TIME_QUERY_EXEC 30
CThreadTargetSelect::CThreadTargetSelect( CDataQueue<CDataAccess> * const pQueue, unsigned int uTargetService )
: m_pQueue(pQueue), m_uTargetService(uTargetService)
{
// 멤버변수 초기화
m_threadHandle = 0;
m_uSleep = SLEEP_TIME_DEFAULT;
m_ullResourceIdMin = 0;
m_ullResourceIdMax = 0;
m_ullResourceIdStart = 0;
}
CThreadTargetSelect::~CThreadTargetSelect()
{
// 내부 therad 가 동작 중인 경우... 중지 처리
if( m_threadHandle != 0 )
{
pthread_cancel( m_threadHandle );
}
sleep( 1 ); // thread 종료 잠시 대기
// 혹시나 해서 정석대로 내부 변수 NLLL 처리
if( m_pQueue != NULL )
m_pQueue = NULL;
}
/////////////////////////////////////////////////
bool CThreadTargetSelect::Start( void )
{
// 멤버 변수 유효성 check
if( m_pQueue == NULL )
{
LOG( LERR, "ThreadTargetSelect: content queue pointer is null." );
return false;
}
// RCDB 접속 정보 load
m_rcdbInfo.Load();
// 작업 thread 생성
int nResult = pthread_create( &m_threadHandle, NULL, CThreadTargetSelect::threadFunc, this );
if( nResult != 0 )
{
// thread 생성 실패시
int errorNum = errno;
LOG( LERR, "ThreadTargetSelect: thread create failed. [%d: %s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
///////////////////////////////////////////////
void * CThreadTargetSelect::threadFunc( void * arg )
{
CThreadTargetSelect * pObject = reinterpret_cast<CThreadTargetSelect *> ( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 지정된 주기로 실행
sleep( pObject->m_uSleep );
}
// thread 종료시
pObject->m_threadHandle = 0;
return NULL;
}
void CThreadTargetSelect::Execute( void )
{
// sleep time 조정 처리
m_uSleep = SLEEP_TIME_DEFAULT;
// 전체 구간 scan 이 완료된 경우...
if( m_ullResourceIdStart > m_ullResourceIdMax )
{
m_uSleep = SLEEP_TIME_MAX;
_LOG( LINF, "[SELECT ] content select completed. max sleep[%u]. resource_id min:%llu max:%llu next:%llu"
, m_uSleep, m_ullResourceIdMin, m_ullResourceIdMax, m_ullResourceIdStart );
return;
}
// 조회 queue 상에 저장 가능 공간이 있는지 확인
if( m_pQueue->GetFreeSize() < DB_SELECT_LIMIT )
{
m_uSleep = SLEEP_TIME_MAX;
_LOG( LINF, "[SELECT ] queue free size[%u / %u] too lower. select job skip and max sleep %u sec."
, m_pQueue->GetFreeSize(), m_pQueue->GetMaxSize(), m_uSleep );
return;
}
_LOG( LINF, "[SELECT ] start with target svc[%u], queue_free[%u]"
, m_uTargetService, m_pQueue->GetFreeSize() );
// DB 처리를 수행할 객체 생성
// DB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
DataBase db;
// DB 와 연결을 시도한다.
if( db.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort, m_rcdbInfo.m_strRcdbName
, m_rcdbInfo.m_strRcdbAcct, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
{
// 연결 실패시...
m_uSleep = SLEEP_TIME_DEFAULT;
LOG( LERR, "[SELECT ] db connection failed. sleep %u sec.", m_uSleep );
return;
}
// resource_id 정보를 추출한다.
if( m_ullResourceIdMin == 0 && m_ullResourceIdMax == 0 )
{
char szQuery[4096];
int nResult = 0;
// resource_id 정보 조회
snprintf( szQuery, 4096 - 1
, "SELECT MIN(resource_id), MAX(resource_id) FROM t_access_%u "
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' ; "
, m_uTargetService
);
// Query 실행
db.PgDoExec( szQuery );
nResult = db.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// query 수행 실패시
LOG( LERR, "[SELECT ] service[%u] resource_id min/max select error. [%d][%s][%s]"
, m_uTargetService, nResult, db.GetErrorMessage().c_str(), szQuery );
// DB 연결 해제
db.PgCloseDB();
// 대부분 t_access_xxx table 이 존재하지 않는 경우 이므로...
// 재시도 시간을 최대한 지연시킨다.
m_uSleep = SLEEP_TIME_MAX;
return;
}
// 추출된 정보 저장
m_ullResourceIdMin = atoll( db.GetValue( 0, 0 ) );
m_ullResourceIdMax = atoll( db.GetValue( 0, 1 ) );
// resource_id 정보 최초 추출시.. start 시점은 최소값으로 설정 처리
m_ullResourceIdStart = m_ullResourceIdMin;
// db resultset clear.
db.PgClear();
_LOG( LINF, "[SELECT ] service[%u] resource_id min: %llu max: %llu"
, m_uTargetService, m_ullResourceIdMin, m_ullResourceIdMax );
}
// DB 에서 access time 추출 대상 content 목록을 조회하여 queue 에 저장 처리한다.
SelectContent( db );
// DB 와의 연결을 명시적으로 해제 처리
db.PgCloseDB();
return;
}
bool CThreadTargetSelect::SelectContent( DataBase & db )
{
// Query 수행 시간 check 를 위한 변수
struct timespec timeStart, timeNow;
double timeElapsed = 0.0;
memset( &timeStart, 0x00, sizeof( struct timespec ) );
memset( &timeNow, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &timeStart );
_LOG( LINF, "[SELECT ] service[%u] content select start with start resource_id: %llu"
, m_uTargetService, m_ullResourceIdStart );
// DB 연결은 이미 되어 있는 상태
char szQuery[4096];
int nResult = 0;
// 대상 content 조회
snprintf( szQuery, 4096 - 1
, "SELECT resource_id, uri, host_name, filename_hash "
"FROM t_access_%u "
"WHERE resource_id >= %llu AND resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' AND file_lastaccess = 0 "
"ORDER BY resource_id ASC LIMIT %d"
, m_uTargetService
, m_ullResourceIdStart
, DB_SELECT_LIMIT
);
// Query 실행
db.PgDoExec( szQuery );
nResult = db.PgResult( DataBase::NOT_CLEAR );
// Query 수행시간이 지정된 시간 이상 걸리면 로깅....
clock_gettime( CLOCK_MONOTONIC, &timeNow );
timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;
if( timeElapsed >= (double)LOGGING_MAX_TIME_QUERY_EXEC )
{
_LOG( LWAR, "[SELECT ] query execute time too long [%.2lf sec]. result[%d] [%s]"
, timeElapsed, nResult, szQuery );
}
else
{
_LOG( LDBG, "[SELECT ] query execute [%.2lf sec]. result[%d] [%s]"
, timeElapsed, nResult, szQuery );
}
if( nResult < 0 )
{
// query 수행 실패시
LOG( LERR, "[SELECT ] content select exec error. [%d][%s][%s]"
, nResult, db.GetErrorMessage().c_str(), szQuery );
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
db.PgClear();
// 재시도 시간 최대 설정
m_uSleep = SLEEP_TIME_MAX;
return false;
}
unsigned int uPushTotalCount = 0;
// 결과 Row 수 확인
nResult = db.GetNoTuples();
_LOG( LINF, "[SELECT ] content selected. [svc:%u select_count:%d ]"
, m_uTargetService, nResult );
if( nResult < 0 )
{
// query 수행 실패시
LOG( LERR, "[SELECT ] content select tuple error. [%d][%s][%s]"
, nResult, db.GetErrorMessage().c_str(), szQuery );
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
db.PgClear();
// 재시도 시간 최대 설정
m_uSleep = SLEEP_TIME_MAX;
return false;
}
else if( nResult == 0 )
{
// 적합한 Content 가 없는 경우..
LOG( LWAR, "[SELECT ] content select not found. [%d][svc:%u][%s]"
, nResult, m_uTargetService, szQuery );
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
db.PgClear();
// 재시도 시간 최대 설정
m_uSleep = SLEEP_TIME_MAX;
// 더 이상 작업할 contnet 가 없으므로.. 더 이상 DB 조회 발생하지 않도록 처리
m_ullResourceIdStart = m_ullResourceIdMax + 1;
return false;
}
else
{
// N 개의 조회 결과가 존재하는 경우
// - 정해진 용량까지 Queue 에 push 처리한다.
CDataAccess data;
for( int i = 0; i < nResult; i++ )
{
// 0: resouce_id
// 1: uri
// 2: host_name
// 3: filename_hash
// 확인 결과 resouce_id 의 최대값은 9223372036854775807
// long long int 의 최대값은 9223372036854775807 으로 resouce_id 값과 동일
// 실제 변환 테스트해 보니 최대값까지 정상 변환 확인함.
data.m_uResourceId = atoll( db.GetValue( i, 0 ) );
data.m_strUri = db.GetValue( i, 1 );
data.m_strHostname = db.GetValue( i, 2 );
data.m_strFilenamehash = db.GetValue( i, 3 );
// push 처리
if( m_pQueue->Push( data ) == false )
{
// push 처리 실패시...
// 2 sec 후 재시도해서 실패하면.. queue full 가능성이 있으므로...
// 버리고.. loop 중지
sleep( 2 );
if( m_pQueue->Push( data ) == false )
{
LOG( LWAR, "[SELECT ] content select ok. but queue push fail. [svc:%u resource_id:%llu]"
, m_uTargetService, data.m_uResourceId );
break;
}
}
// total push count 1 증가
uPushTotalCount++;
// 로깅 for debug
//_LOG( LINF, "[SELECT ] content select, push ok. [svc:%u resource_id:%llu]", m_uTargetService, data.m_uResourceId);
}
// 다음 조회 start resouce_id 정보 설정
m_ullResourceIdStart = data.m_uResourceId + 1;
}
db.PgClear();
// Content 선택 결과 로깅
_LOG( LINF, "[SELECT ] content push end. service[%u] select[%d][%.2lf sec] push[%u] netxt start resource_id[%llu]"
, m_uTargetService, nResult, timeElapsed, uPushTotalCount, m_ullResourceIdStart );
return true;
}
+83
View File
@@ -0,0 +1,83 @@
/****************************************************************************
rc_accessd target content select thread header
-----------------------------------------
begin : 2021/04/12
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __THREAD_TARGET_SELECT_H__
#define __THREAD_TARGET_SELECT_H__
#include <pthread.h>
#include "DataAccess.h"
#include "DataQueue.hpp"
#include "Database.h"
#include "RcdbInfo.h"
// CThreadTargetSelect class
// - access time 추출 대상 content 를 DB 로 부터 조회하여....
// - 전달 받은 queue 상에 push 처리
class CThreadTargetSelect
{
public:
CThreadTargetSelect( CDataQueue<CDataAccess> * const pQueue , unsigned int uTargetService );
~CThreadTargetSelect();
// theread 생성하여 내부 job 을 주기적으로 수행 처리한다.
bool Start( void );
private:
// thread handle
pthread_t m_threadHandle;
// thread loop 마다 sleep time 를 저장하기 위한 변수
unsigned int m_uSleep;
// 추출된 content 목록을 저장하기 위한 queue 로서... 생성자를 통해 전달받은 queue point 를 의미
CDataQueue<CDataAccess> * m_pQueue;
// DB 연결 정보 관리 객체
CRcdbInfo m_rcdbInfo;
// 추출 대상 서비스 정보
unsigned int m_uTargetService;
// 추출 구간 처리를 위한 resource_id 정보
unsigned long long m_ullResourceIdMin;
unsigned long long m_ullResourceIdMax;
unsigned long long m_ullResourceIdStart;
private:
// thread loop base function
static void * threadFunc( void * arg );
// 실제 content 조회 처리를 담당하는 함수
void Execute( void );
// 지정된 Service 에 대해 content 목록을 추출하여 queue 에 저장 처리한다.
bool SelectContent( DataBase & db );
};
#endif /* __THREAD_TARGET_SELECT_H__ */
+180
View File
@@ -0,0 +1,180 @@
#include "WorkerAccess.h"
#include "Logger.h"
#include "ProcessRename.h"
#include "ProcessConfig.h"
#include "DataQueue.hpp"
#include "DataAccess.h"
#include "ThreadTargetSelect.h"
#include "ThreadExtractControl.h"
#include "ThreadDbUpdate.h"
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <time.h>
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerAccess()
{
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Worker Access 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerAccessTerminate( int nSignalNumber )
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "[Access:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "[Access:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
ReadyToExitWorkerAccess();
// 종료전 잠시 대기
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
// Worker::Access Process 의 signal 처리기...
void SetWorkerAccessSignalHandler()
{
sigset_t set;
struct sigaction act;
memset( &act, 0x00, sizeof( act ) );
sigfillset( &set );
sigprocmask( SIG_SETMASK, &set, NULL );
sigfillset( &act.sa_mask );
// 무시 처리 signal
act.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &act, NULL ); /* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
sigaction( SIGHUP, &act, NULL ); /* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
sigaction( SIGINT, &act, NULL ); /* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
sigaction( SIGQUIT, &act, NULL ); /* 키보드에 의한 Abort 신호 처리 => ? */
// Worker 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
act.sa_handler = SIG_IGN;
sigaction( SIGCHLD, &act, NULL );
/* 각종 에러나 사용자의 종료 신호 처리 */
act.sa_handler = SignalWorkerAccessTerminate;
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
// 나머지 신호는 default 처리.
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask( SIG_SETMASK, &set, NULL );
return;
}
// Worker::Access 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerAccessMain( void )
{
// Process Rename..
set_ps_display( PROG_NAME": Access init", false );
// Set Signal Handler
SetWorkerAccessSignalHandler();
// 1. 사용할 객체 초기화 처리
// DB 에서 조회된 access time 추출 대상 content 목록 정보를 저장할 queue 객체
CDataQueue<CDataAccess> queueTarget;
queueTarget.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
// access time 정보가 추출된 content 목록 정보를 저장할 queue 객체
CDataQueue<CDataAccess> queueComplete;
queueComplete.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
// access time 추출 대상 조회를 수행하는 thread 생성
CThreadTargetSelect threadTargetSelect( &queueTarget, CProcessConfig::GetInstance()->GetAccessTimeExtractService() );
// access time 추출 thread 를 control 하는 thread 객체 생성
CThreadExtractControl threadExtractControl( &queueTarget, &queueComplete, CProcessConfig::GetInstance()->GetMaxThreadCount() );
// 추출된 access time 정보를 DB 에 update 처리를 수행하는 thread 객체 생성
CThreadDbUpdate threadDbUpdate( &queueComplete, CProcessConfig::GetInstance()->GetAccessTimeExtractService() );
// 2. 각 모듈 기동 처리
set_ps_display( PROG_NAME": Access start", false );
// DB udpate 모듈 start
if( threadDbUpdate.Start() == false )
{
_LOG( LWAR, "[Access:%d] db update thread start failed. -> Process exit.", getpid() );
ReadyToExitWorkerAccess();
// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
return EXIT_FAILURE;
}
// access time 추출을 담당하는 thrad 객체 start
if( threadExtractControl.Start() == false )
{
_LOG( LWAR, "[Access:%d] content access time extract control thread start failed. -> Process exit.", getpid() );
ReadyToExitWorkerAccess();
// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
return EXIT_FAILURE;
}
// access time 추출 대한 content 조회 thread start
if( threadTargetSelect.Start() == false )
{
_LOG( LWAR, "[Access:%d] content select thread start failed. -> Process exit.", getpid() );
ReadyToExitWorkerAccess();
// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
return EXIT_FAILURE;
}
// 대기
while( 1 )
{
// 할일 없어서.. 시그널 수신까지 대기 한다.
pause();
}
// 종료시 정리 작업 수행
// - 실제로는 동작하지 않는 code
ReadyToExitWorkerAccess();
return EXIT_SUCCESS;
}
+37
View File
@@ -0,0 +1,37 @@
/****************************************************************************
access time extract worker process main
-----------------------------------------
begin : 2019/04/05
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __WORKER_ACCESS_H__
#define __WORKER_ACCESS_H__
#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
// Worker::Access 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerAccessMain( void );
#ifdef __cplusplus
}
#endif
#endif /* __WORKER_ACCESS_H__ */
+5
View File
@@ -0,0 +1,5 @@
// sample 소스 파일
#include ""
+32
View File
@@ -0,0 +1,32 @@
/****************************************************************************
rc_apid header
-----------------------------------------
begin : 2021/03/09
copyright : (C) 2005 Solbox Inc.
author : huibong
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __APID_DATA_H__
#define __APID_DATA_H__
#include <>
// class 설명
class C
{
};
#endif /* __APID_DATA_H__ */
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
#
# Description : process compile test by huibong
#
PROG_NAME=rc_accessd
killall $PROG_NAME
gmake clean
killall $PROG_NAME
gmake
sleep 1
./$PROG_NAME -c ../rc_accessd.conf
sleep 1
ps aux | grep $PROG_NAME | grep -v grep | grep -v tail