base
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
|
||||
Revision 1531
|
||||
-------------------
|
||||
수정일 : 2020-01-19
|
||||
수정자 : 유희곤
|
||||
|
||||
- CHG: move 대상 FHS 선정시 disk busy 허용값을 conf 로 설정 가능하도록 기능 추가 (#33395)
|
||||
- move 대상 FHS 선정시 기존 30% 로 하드코딩 되어 있었으나...
|
||||
- PC-71 등의 bigbox 장비에서는 disk가 많아 disk busy 발생 가능성도 높아짐
|
||||
- 따라서 운영자가 조정할 수 있도록 disk busy 설정을 conf 로 변경
|
||||
- gstat 의 표시 내역과 동일하게.. disk busy max default 값은 50, 최대 80% 까지 설정 가능하도록 수정
|
||||
|
||||
|
||||
Revision 1529
|
||||
-------------------
|
||||
수정일 : 2020-01-13
|
||||
수정자 : 유희곤
|
||||
|
||||
- NEW: 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
- 분산 대상 선택된 content 가 이미 대상 장비에 복제본 등이 존재하여 이동이 실패할 경우....
|
||||
- 다음 조회시에도 계속 같은 content 가 다시 선택되는 문제가 발생함.
|
||||
- 따라서 복제본 존재로 인한 이동 실패가 N 회 발생하는 경우...
|
||||
- 이동 대상 content 를 Big size order 가 아닌 RANDOM 방식으로 조회하여 분산 처리하는 하는 mode 기능 추가함
|
||||
- RANDOM mode 로 변경 후 3시간 후 다시 default Size Order mode 로 전환 처리됨
|
||||
|
||||
- CHG: 이동 대상 FHS 재선택 방지 time limit 조정 (#33306)
|
||||
- 이동 대상 FHS 의 재선택 방지 시간을 default 30 분 -> 10 분으로 변경
|
||||
- 이동 대상 FHS 의 재선택 방지 최소 설정 가능 시간을 10분 -> 5분으로 변경
|
||||
|
||||
|
||||
Revision 1470
|
||||
-------------------
|
||||
수정일 : 2019-09-10
|
||||
수정자 : 유희곤
|
||||
|
||||
- NEW: SVN 신규 등록
|
||||
- 신규 rc_balanced 에 대한 SVN 등록
|
||||
- 개발, 테스트는 2019-04월경에 완료되었지만..
|
||||
테스트 및 RCTS 패키징 일괄 작업으로 배포가 지연됨
|
||||
- 본 모듈 관련 자세한 사항은 redmine #32490 이슈 참고
|
||||
- 설계서: svn://svc1svn.solbox.com/document/01 양방향/02 설계서/26 Content Auto Rebalancing/rc_balanced.vsdx
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#****************************************************************************
|
||||
# Makefile for rc_balanced
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2019/02/22
|
||||
# copyright : (C) 2005 Solbox.Inc
|
||||
# author : Dev Storage Team
|
||||
# email : huibong@solbox.com
|
||||
# version : 3.5.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.
|
||||
#*****************************************************************************
|
||||
|
||||
SUBDIRS = lib src
|
||||
|
||||
.PHONY: all $(SUBDIRS)
|
||||
|
||||
|
||||
all: $(SUBDIRS)
|
||||
sync;
|
||||
|
||||
|
||||
$(SUBDIRS):
|
||||
$(MAKE) all -C $@
|
||||
|
||||
|
||||
install:
|
||||
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) install); done
|
||||
|
||||
|
||||
clean:
|
||||
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) clean); done
|
||||
|
||||
|
||||
# End of Makefile
|
||||
@@ -0,0 +1,67 @@
|
||||
/***************************************************************************
|
||||
ftsd control interface ( File Replication & Cache & Move & Delete Control) Header ( FtsdProtocol.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 3.2.0.R0811
|
||||
|
||||
CopyRight(C) 2005 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 __FTSD_PROTOCOL_H__
|
||||
#define __FTSD_PROTOCOL_H__
|
||||
|
||||
|
||||
struct FileTransferPacketHeader {
|
||||
|
||||
char stx; // Packet 유효성 관리 코드
|
||||
char type; // Request or Response 여부 ( 0x00: Request, 0x01: Response )
|
||||
char command[4]; // Command Code ( 4 Byte) : 0th control-ftsd, 1th ftsd-ftsd 사용.
|
||||
char result[4]; // Result Code ( 4 Byte )
|
||||
unsigned int data_length; // Packet Data 부분의 길이값 ( Network Byte Order 사용)
|
||||
char extend_code[2]; // 확장 및 Padding bits ( 2 Byte )
|
||||
};
|
||||
|
||||
|
||||
// Packet Header stx 코드
|
||||
#define HEADER_STX_CODE 0x02
|
||||
|
||||
// Packet Header type 구분코드
|
||||
#define HEADER_TYPE_REQUEST 0x00
|
||||
#define HEADER_TYPE_RESPONSE 0x01
|
||||
|
||||
|
||||
// Packet command 공통.
|
||||
#define COMMON_ALIVE_CHECK 0x7F
|
||||
|
||||
// Packet command : control 프로세스 <-> ftsd 통신 ( Command 첫번째 Byte 만 사용 )
|
||||
#define NOT_CONTROL_COMMAND 0x00 // Control 이 전송한 요청이 아닌 경우.
|
||||
#define CONTROL_FILE_REPLICATION 0x01 // Content 복제 생성 요청
|
||||
#define CONTROL_FILE_CACHE 0x02 // Content 에 대해 Cache 폴더로 복제 요청
|
||||
|
||||
#define CONTROL_FILE_CHECK 0x03 // Content 에 대한 존재 여부, Size 확인 및 Hash 추출 요청
|
||||
#define CONTROL_FILE_UNLINK 0x04 // Content 에 대한 unlink 처리 요청
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460)
|
||||
#define CONTROL_FILE_CHECK_TOTO 0x05 // 토토디스크 고객사 지원을 위한 MD5 Hash 추출 기능 (2016-05-03 추가)
|
||||
|
||||
|
||||
// Packet Result : type이 Reponse 인 경우에만 세팅됨.( 첫번째 Byte 만 사용시 )
|
||||
#define HEADER_RESULT_SUCCESS 0x00
|
||||
#define HEADER_RESULT_ERROR 0x01
|
||||
|
||||
|
||||
// 기타 정보
|
||||
#define LENGTH_FIELD_SIZE 4 // 가변데이터 형식 사용시 Length 필드의 메모리 크기 (unsigned int)
|
||||
|
||||
|
||||
// unlink 요청시 Mode 설정 정보
|
||||
#define UNLINK_MODE_NORMAL 0 // 해당 Content 가 존재하고 size 값이 정확한 경우에만 삭제, 나머지는 오류
|
||||
#define UNLINK_MODE_FORCE 1 // 해당 Content 가 존재하지 않거나.. Size 값이 틀려도 강제로 삭제 처리.. 오류로 처리되는 상황은 통신, 시스템 오류 발생시.
|
||||
|
||||
|
||||
#endif /* __FTSD_PROTOCOL_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
/***************************************************************************
|
||||
ftsd control interface ( File Replication & Cache & Move & Delete Control) Header ( FtsdSocketControl.h )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/09
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 3.2.0.R0811
|
||||
|
||||
CopyRight(C) 2005 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 __FTSD_SOCKET_CONTROL_H__
|
||||
#define __FTSD_SOCKET_CONTROL_H__
|
||||
|
||||
#include "BaseSocket.h"
|
||||
#include "FtsdProtocol.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
|
||||
///< BYTE 타입 정의
|
||||
#ifndef _BYTE_DEFINED
|
||||
#define _BYTE_DEFINED
|
||||
typedef unsigned char BYTE;
|
||||
#endif // _BYTE_DEFINED
|
||||
|
||||
|
||||
#define DEFAULT_SOCKET_TEMP_BUFFER_SIZE 1024 // SocketControl 에서 사용할 임시버퍼 크기.
|
||||
|
||||
class CFtsdSocketControl : public CBaseSocket
|
||||
{
|
||||
private:
|
||||
|
||||
/// @brief Packet Header 변수
|
||||
struct FileTransferPacketHeader m_packetHeader;
|
||||
|
||||
/// @brief m_packetHeader 구조체의 크기를 저장하기 위한 상수
|
||||
const int m_nPacketHeaderLen;
|
||||
|
||||
/// @brief Packet Header 에 저장된 Data 부분의 길이 정보값.
|
||||
unsigned int m_nPacketDataLen;
|
||||
|
||||
/// @brief Packet Data 부분의 수신처리시 임시로 사용할 버퍼.
|
||||
BYTE m_tempBuffer[DEFAULT_SOCKET_TEMP_BUFFER_SIZE];
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// @brief 생성자.
|
||||
CFtsdSocketControl();
|
||||
|
||||
/// @brief 소멸자.
|
||||
~CFtsdSocketControl();
|
||||
|
||||
|
||||
/// @brief 전달받은 Target 으로 Socket 접속을 수행
|
||||
/// @param szTarget [in] 접속 대상 Host name 또는 IP
|
||||
/// @param nPort [in] 접속 Port
|
||||
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
|
||||
bool ConnectTarget( const std::string& szTarget, int nPort );
|
||||
|
||||
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 File Replication 명령 전송.
|
||||
/// @param szFileName [in] 복제할 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param nFileSize [in] 복제할 원본 Source 파일의 크기.
|
||||
/// @param szTargetTranId [in] RC 간 복제 처리시 변경할 대상 Tran ID 정보 값.
|
||||
/// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보 ( conf 파일에 지정됨)
|
||||
/// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부 \n
|
||||
///< 해당값을 true 로 지정시 ftsd 상에서 내부망을 우선 사용하여 파일 복제 시도
|
||||
///< 만약 내부망 사용 불가시 자동으로 외부망 사용.
|
||||
///< RC-RC 간 처리시에는 사용하지 않도록 false 로 지정할 것.
|
||||
/// @param vecTargetFhs [in] 복제 대상 Target FHS Host Name 정보
|
||||
/// @return Replication 복제 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs);
|
||||
bool SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize, const std::string& szTargetTranId, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs);
|
||||
|
||||
|
||||
/// @brief File Replication 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Replication 이 정상적으로 수행되었는지 여부 \n
|
||||
///< trnsfer 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< mapSuccessFhs, mapFailFhs 상에 관련 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param mapSuccessFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Replication 처리에 성공한 FHS 및 저장된 File 이름 정보를 저장
|
||||
/// @param mapFailFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Replication 처리에 실패한 FHS Host Name 및 발생된 오류메시지 정보를 저장.
|
||||
///
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 로부터
|
||||
///< Replication 에 대한 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 복제 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 로 부터 Data 을 수신하였으나 File Replication 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileReplicationResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, std::map< std::string, std::string>& mapSuccessFhs, std::map< std::string, std::string>& mapFailFhs );
|
||||
|
||||
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 File Cache 명령 전송.
|
||||
/// @param szFileName [in] Cache 처리할 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param nFileSize [in] Cache 처리할 원본 Source 파일의 크기.
|
||||
/// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보 ( conf 파일에 지정됨)
|
||||
/// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부 \n
|
||||
///< 해당값을 true 로 지정시 ftsd 상에서 내부망을 우선 사용하여 파일 복제 시도
|
||||
///< 만약 내부망 사용 불가시 자동으로 외부망 사용.
|
||||
/// @param vecTargetFhs [in] Cache 대상 Target FHS Host Name 정보
|
||||
/// @return Cache 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileCacheRequest(const std::string& szFileName, unsigned long long nFileSize, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs);
|
||||
|
||||
|
||||
/// @brief File Cache 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Cache 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< trnsfer 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< mapSuccessFhs, mapFailFhs 상에 관련 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param mapSuccessFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Cache 처리에 성공한 FHS 및 저장된 File 이름 정보를 저장
|
||||
/// @param mapFailFhs [out] bSuccess == true 인 경우 \n
|
||||
///< Cache 처리에 실패한 FHS Host Name 및 발생된 오류메시지 정보를 저장.
|
||||
///
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 로부터
|
||||
///< Cache 에 대한 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Cache 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 로 부터 Data 을 수신하였으나 File Cache 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileCacheResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, std::map< std::string, std::string>& mapSuccessFhs, std::map< std::string, std::string>& mapFailFhs );
|
||||
|
||||
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content Check 명령 전송.
|
||||
/// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param bHashCheck [in] Hash 값을 추출할지 여부
|
||||
/// @param nHashCheckSize [in] Hash 값을 추출할 경우 Size 설정값 ( MByte 단위 )\n
|
||||
///< 0 : Content 에 대한 전체 Hash 값을 추출함.
|
||||
///< 숫자 : Content 의 앞 부분부터 지정된 크기 (MByte ) 까지 Hash 값을 추출
|
||||
/// @return Check 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileCheckRequest(const std::string& szFileName, bool bHashCheck, unsigned long long nHashCheckSize );
|
||||
|
||||
|
||||
|
||||
/// @brief File Check 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Check 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< ftsd 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< nFileSize, szHashValue 상에 결과 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param nFileSize [out] bSuccess == true 인 경우 요청한 Content 에 대한 file size 정보를 저장. ( Byte 단위 )
|
||||
/// @param szHashValue [out] bSuccess == true 이고... Hash 값 추출을 요청한 경우 추출된 Hash 값 정보를 저장.
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 으로부터 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Check 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 으로 부터 Data 을 수신하였으나 File Check 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileCheckResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, unsigned long long& nFileSize, std::string& szHashValue );
|
||||
|
||||
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Unlink 명령 전송.
|
||||
/// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @param nFileSize [in] 원본 파일의 크기 ( Byte 단위 )
|
||||
/// @param bForceUnlink [in] 강제 삭제 처리 여부 ( Defalult false )
|
||||
/// false : 일반모드 - 해당 파일이 존재하고 Size 값이 동일할 경우에만 삭제처리.. 나머지는 오류로 처리.
|
||||
/// true : 강제모드 - 해당 파일이 존재하지 않거나.. Size 가 틀려도 강제로 삭제 처리.. 오류는 통신, 시스템 오류 발생시에만
|
||||
/// @return Unlink 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileUnlinkRequest(const std::string& szFileName, unsigned long long nFileSize, bool bForceUnlink = false );
|
||||
|
||||
/// @brief File Unlink 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File unlink 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< ftsd 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 따로 수신하는 Data 는 없음. \n
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 으로부터 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Unlink 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 으로 부터 Data 을 수신하였으나 File Unlink 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileUnlinkResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage );
|
||||
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460)
|
||||
/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content Check 명령 전송. (토토디스크 지원용)
|
||||
/// @param szFileName [in] 원본 Source 파일명 (/stg/node0/186/abcde..)
|
||||
/// @return Check 요청 메시지 전송 성공시 true, 실패시 false 반환.
|
||||
bool SendFileCheckTOTORequest( const std::string& szFileName );
|
||||
|
||||
|
||||
// NEW 2016-05-04 huibong 토토디스크 지원용 기능 추가 (#27460, #27533)
|
||||
/// @brief File Check 요청에 대한 처리 결과를 ftsd 로 부터 수신한다.
|
||||
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
|
||||
///< 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
|
||||
/// @param bSuccess [out] File Check 처리가 정상적으로 수행되었는지 여부 \n
|
||||
///< ftsd 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
|
||||
///< szErrorMessage 변수상에 오류내용이 저장됨. \n
|
||||
///< 정상적으로 처리된 경우 true 가 저장되며 \n
|
||||
///< nFileSize, szHashValue 상에 결과 정보가 저장됨.
|
||||
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
|
||||
/// @param nFileSize [out] bSuccess == true 인 경우 요청한 Content 에 대한 file size 정보를 저장. ( Byte 단위 )
|
||||
/// @param szTotoMd5 [out] bSuccess == true 인 경우 추출된 totorasa 용 MD5 Hash 값 정보를 저장.
|
||||
/// @param szChecksum [out] bSuccess == true 인 경우 추출된 totorasa 용 Checksum 정보를 저장.
|
||||
/// @param szDnaCheckKey [out] bSuccess == true 인 경우 추출된 totorasa 용 DNA Check key 값 정보를 저장.
|
||||
/// @return -1 : Socket 통신 관련 오류 발생하여 ftsd 와 연결이 끊어진 경우 \n
|
||||
///< => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
|
||||
///<
|
||||
///< 0 : 입력변수인 nTimeout 에 지정된 시간안에 ftsd 으로부터 처리 결과 정보를 수신하지 못한 경우. \n
|
||||
///< 이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
|
||||
///< 이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
|
||||
///< 응답을 대기할 수 있다. \n
|
||||
///<
|
||||
///< 1 : 파일 Check 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
|
||||
///<
|
||||
///< 2 : ftsd 으로 부터 Data 을 수신하였으나 File Check 요청에 대한 응답이 아닌 경우 \n
|
||||
///< 해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
|
||||
///< 본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
|
||||
int GetFileCheckTOTOResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage
|
||||
, unsigned long long& nFileSize, std::string& szTotoMd5, std::string& szChecksum, std::string& szDnaCheckKey );
|
||||
|
||||
|
||||
|
||||
/// @brief 연결된 Socket 통신을 이용하여 내부적으로 정의된 Alive Check 패킷 전송 \n
|
||||
///< => 해당 패킷에 대한 처리는 내부적으로 처리되어 결과값을 확인할 필요는 없다.
|
||||
/// @return Socket 연결 해제 또는 전송 관련 오류 발생시 false, 전송 성공시에는 true 반환.
|
||||
bool SendAliveCheck(void);
|
||||
|
||||
/// @brief Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
|
||||
void PrintHeaderToLog(void);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Packet Header 부분의 수신 처리를 위한 함수. Alive Check 요청 패킷은 자동으로 무시처리함.
|
||||
/// @param timeout [in] 대기시간.
|
||||
/// @retrun 성공시 true, 오류 발생및 실패시 fasle 반환.
|
||||
bool GetPacketHeader(int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT);
|
||||
|
||||
/// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리.
|
||||
/// @param size [in] read 할 데이터 크기
|
||||
/// @param value [out] 읽은 데이터를 저장할 string 변수
|
||||
/// @return On success return true, otherwise return false.
|
||||
bool GetPacketData( unsigned int& size, std::string& value );
|
||||
|
||||
/// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 내부 임시버퍼인 m_tempBuffer 에 저장처리.
|
||||
/// @param size [in] read 할 데이터 크기
|
||||
/// @return On success return true, otherwise return false.
|
||||
bool GetPacketData( unsigned int size );
|
||||
|
||||
/// @brief pValue 에 저장된 데이터를 unsigned int 형으로 변환처리 및 Endian 변환
|
||||
unsigned int GetDataToUInt( BYTE * pValue, bool bConvertEndian = true );
|
||||
|
||||
/// @brief pValue 에 저장된 데이터를 unsigned long long (64Byte) 형으로 변환처리.
|
||||
unsigned long long GetDataToUInt64( BYTE * pValue );
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __FTSD_SOCKET_CONTROL_H__ */
|
||||
|
||||
@@ -0,0 +1,595 @@
|
||||
#include "BaseSocket.h"
|
||||
|
||||
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#define SOCKET_NOT_VALID -1
|
||||
#define MAX_SEND_RETRY_COUNT 10 // Socket send 실패시 최대 재전송 시도 횟수.
|
||||
|
||||
|
||||
/// @brief 생성자.
|
||||
/// @param socket [in] 처리할 socket descriptor
|
||||
CBaseSocket::CBaseSocket( const int& socket )
|
||||
: m_sock( socket )
|
||||
{
|
||||
if( m_sock < 0 )
|
||||
{
|
||||
m_sock = SOCKET_NOT_VALID;
|
||||
m_bConnected = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief 소멸자
|
||||
CBaseSocket::~CBaseSocket()
|
||||
{
|
||||
// 소멸자 Socket 명시적 Close 처리.
|
||||
Close();
|
||||
}
|
||||
|
||||
/// @brief 소켓의 Close 처리를 수행함.
|
||||
void CBaseSocket::Close()
|
||||
{
|
||||
m_bConnected = false;
|
||||
|
||||
if( m_sock != SOCKET_NOT_VALID )
|
||||
{
|
||||
close( m_sock );
|
||||
m_sock = SOCKET_NOT_VALID;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief 전달받은 Target 으로 Socket 접속을 수행
|
||||
/// @param szTarget [in] 접속 대상 Host name 또는 IP
|
||||
/// @param nPort [in] 접속 Port
|
||||
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
|
||||
bool CBaseSocket::Connect( const std::string& szTarget, int nPort )
|
||||
{
|
||||
// 기존 접속을 Close 처리
|
||||
Close();
|
||||
|
||||
unsigned int nHost = ConversionAddr( szTarget.c_str() );
|
||||
if( nHost == INADDR_NONE )
|
||||
{
|
||||
LOG( LERR, "target host name resolve fail. [%s]->INADDR_NONE", szTarget.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in stTargetAddr;
|
||||
|
||||
m_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if( m_sock == -1 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
|
||||
LOG( LERR, "socket create failed.[%d][%s] Target:[%s]", errorNum, strerror(errorNum), szTarget.c_str() );
|
||||
|
||||
m_sock = SOCKET_NOT_VALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
stTargetAddr.sin_family = AF_INET;
|
||||
stTargetAddr.sin_addr.s_addr = nHost;
|
||||
stTargetAddr.sin_port = htons( nPort );
|
||||
|
||||
// NEW 2012-05-18 huibong
|
||||
// Connnection 종료시 많은 TIME_WAIT 상태 발생으로 인해 .. 이를 제거하기 위해 SO_LINGER 옵션을 설정처리한다.
|
||||
struct linger opt_linger;
|
||||
opt_linger.l_onoff = 1; /* LINGER ON */
|
||||
|
||||
// CHG 2014-05-27 huibong
|
||||
// 아래와 같이 No Wait 로 처리할 경우... Server 역활 수행 중 Close 신호를 받지 못하는 경우가 발생 가능함.
|
||||
// 따라서 5 sec 정도 대기하도록 수정 처리한다.
|
||||
//opt_linger.l_linger = 0; /* No Wait => 0 for abortive disconnect */
|
||||
opt_linger.l_linger = 5; // 5 sec 대기
|
||||
|
||||
int result = setsockopt( m_sock, SOL_SOCKET, SO_LINGER, &opt_linger, sizeof(opt_linger));
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "setsockopt func SO_LINGER set fail.[%d][%s]", errorNum, strerror(errorNum));
|
||||
|
||||
}
|
||||
|
||||
/* Send Timeout 설정. */
|
||||
struct timeval tv_timeo = { 5, 0 };
|
||||
|
||||
result = setsockopt( m_sock, SOL_SOCKET, SO_SNDTIMEO, &tv_timeo, sizeof(tv_timeo));
|
||||
if( result != 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "setsockopt func SO_SNDTIMEO set fail.[%d][%s]", errorNum, strerror(errorNum));
|
||||
|
||||
}
|
||||
|
||||
if(connect(m_sock, (struct sockaddr *)&stTargetAddr, sizeof(stTargetAddr)) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "connect fail.[%s][%d] [%d][%s]", szTarget.c_str(), nPort, errorNum, strerror( errorNum ) );
|
||||
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bConnected = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief 멤버 변수인 m_sock 이 유효하고 연결된 상태인 경우 true 반환.
|
||||
/// @return socket이 유효하지 않거나 연결이 끊어지 경우 false 반환, 그외에는 true 반환.
|
||||
bool CBaseSocket::IsValidSocket()
|
||||
{
|
||||
if( m_sock != SOCKET_NOT_VALID && m_bConnected == true )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief m_sock 으로부터 지정된 size 만큼 데이터 read 를 시도 ( read 함수와 동일 )
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t CBaseSocket::Read( void * vptr, size_t size )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
while( (nRead = read( m_sock, vptr, size )) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2010-07-23 BUG huibong 잘못된 대입연산자를 비교연산자로 수정.
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return nRead;
|
||||
}
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t CBaseSocket::ReadN( void * vptr, size_t size )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
size_t nLeft = size;
|
||||
char * ptr = (char *)vptr;
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
if( (nRead = read( m_sock, ptr, nLeft )) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류 , -2: Timeout
|
||||
ssize_t CBaseSocket::ReadNTimeout( void * vptr, size_t size, int timeout )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
size_t nLeft = size;
|
||||
char * ptr = (char *)vptr;
|
||||
|
||||
struct timeval timeOver;
|
||||
int result;
|
||||
|
||||
fd_set selectFds;
|
||||
FD_ZERO( &selectFds );
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
timeOver.tv_sec = timeout;
|
||||
timeOver.tv_usec = 0;
|
||||
FD_SET( m_sock, &selectFds );
|
||||
|
||||
result = select( m_sock+1, &selectFds, (fd_set *)NULL, (fd_set *)NULL, &timeOver );
|
||||
|
||||
if( result > 0 )
|
||||
{
|
||||
if( FD_ISSET( m_sock, &selectFds ))
|
||||
{
|
||||
if( (nRead = read( m_sock, ptr, nLeft) ) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
if(errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK)
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
}
|
||||
|
||||
}
|
||||
else if( result == 0 ) // Timeout
|
||||
{
|
||||
//LOG( LDBG, "read timeout");
|
||||
return -2;
|
||||
}
|
||||
else
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "select func error.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
///< 본 함수는 Socket 상에 이미 Data 가 존재하는 경우에만 사용.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
|
||||
ssize_t CBaseSocket::ReadNTimeout2( void * vptr, size_t size, int timeout )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
ssize_t nRead = 0;
|
||||
size_t nLeft = size;
|
||||
char * ptr = (char *)vptr;
|
||||
int errorNum;
|
||||
|
||||
// 우선은 읽기 시도.
|
||||
if( (nRead = read( m_sock, ptr, nLeft) ) < 0 )
|
||||
{
|
||||
errorNum = errno;
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nRead = 0;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
|
||||
// 만약 더 받을 데이터가 존재한다면.
|
||||
if( nLeft > 0 )
|
||||
{
|
||||
struct timeval timeOver;
|
||||
int result;
|
||||
|
||||
fd_set selectFds;
|
||||
FD_ZERO( &selectFds );
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
timeOver.tv_sec = timeout;
|
||||
timeOver.tv_usec = 0;
|
||||
FD_SET( m_sock, &selectFds );
|
||||
|
||||
result = select( m_sock+1, &selectFds, (fd_set *)NULL, (fd_set *)NULL, &timeOver );
|
||||
|
||||
if( result > 0 )
|
||||
{
|
||||
if( FD_ISSET( m_sock, &selectFds ))
|
||||
{
|
||||
if( (nRead = read( m_sock, ptr, nLeft) ) < 0 )
|
||||
{
|
||||
errorNum = errno;
|
||||
if(errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK)
|
||||
{
|
||||
nRead = 0;
|
||||
continue;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "read func fail.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nRead == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nLeft -= nRead;
|
||||
ptr += nRead;
|
||||
}
|
||||
}
|
||||
else if( result == 0 ) // Timeout
|
||||
{
|
||||
//LOG( LDBG, "read timeout");
|
||||
return -2;
|
||||
}
|
||||
else
|
||||
{
|
||||
errorNum = errno;
|
||||
LOG( LERR, "select func error.[%d][%s]", errorNum, strerror(errorNum) );
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/// @brief m_sock 으로 지정된 크기만큼 vptr 의 데이터를 전송 시도.\n
|
||||
///< Send Timeout 옵션 설정으로 Write Timeout 설정 가능. \n
|
||||
///< 지정된 횟수만큼 재전송 실패시 오류로 처리함.
|
||||
/// @param vptr [in] 전달할 데이터를 저장한 변수에 대한 포인터.
|
||||
/// @param size [in] write 하고자 하는 데이터의 크기.
|
||||
/// @return write 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t CBaseSocket::WriteN( const void * vptr, size_t size )
|
||||
{
|
||||
if( IsValidSocket() == false )
|
||||
return 0;
|
||||
|
||||
size_t nLeft;
|
||||
ssize_t nWrite;
|
||||
|
||||
const char * ptr = (const char *)vptr;
|
||||
nLeft = size;
|
||||
|
||||
int nTryCount = 0;
|
||||
|
||||
while( nLeft > 0 )
|
||||
{
|
||||
if( (nWrite = send( m_sock, ptr, nLeft, 0 )) < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
|
||||
if( errorNum == EINTR || errorNum == EAGAIN || errorNum == EWOULDBLOCK )
|
||||
{
|
||||
nWrite = 0;
|
||||
++nTryCount;
|
||||
}
|
||||
else if( errorNum == ECONNRESET || errorNum == EPIPE )
|
||||
{
|
||||
// 2015-08-28 CHG huibong EPIPE(32) 오류 발생시.. 연결 종료로 처리되도록 수정
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG( LERR, "send func fail.[%d][%s]", errorNum, strerror(errorNum));
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else if( nWrite == 0 )
|
||||
{
|
||||
m_bConnected = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( nTryCount > MAX_SEND_RETRY_COUNT )
|
||||
{
|
||||
LOG( LERR, "send func failure due to exceeding count of retry[%d/%d]", nTryCount, MAX_SEND_RETRY_COUNT );
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
nLeft -= nWrite;
|
||||
ptr += nWrite;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
// CHG 2011-05-03 huibong
|
||||
// gethostbyname() 함수가 Thread Safe 하지 않기 때문에
|
||||
// DNS resolve 처리시 잘못된 정보를 반환할 가능성이 존재
|
||||
// 이에 따라 본 함수를 수정처리함.
|
||||
/*
|
||||
unsigned int CBaseSocket::ConversionAddr( const char * name )
|
||||
{
|
||||
struct hostent *he;
|
||||
int max;
|
||||
unsigned int retval;
|
||||
|
||||
if ((retval = inet_addr(name)) != INADDR_NONE)
|
||||
return retval;
|
||||
|
||||
he = gethostbyname(name);
|
||||
if (he == NULL)
|
||||
return INADDR_NONE;
|
||||
|
||||
for (max = 0; he->h_addr_list[max]; max++) ;
|
||||
if (max == 1)
|
||||
return *((unsigned int *)(he->h_addr_list[0]));
|
||||
else
|
||||
return *((unsigned int *)(he->h_addr_list[random() % max]));
|
||||
}
|
||||
*/
|
||||
|
||||
unsigned int CBaseSocket::ConversionAddr( const char * name )
|
||||
{
|
||||
unsigned int retval;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *result = NULL;
|
||||
int error;
|
||||
|
||||
|
||||
// 전달받은 정보가 IP 주소인 경우...
|
||||
// - DNS resolve 할 필요 없이 변환시킨 값을 그대로 사용한다.
|
||||
if( (retval = inet_addr(name)) != INADDR_NONE )
|
||||
return retval;
|
||||
|
||||
// getaddrinfo() 함수 호출을 위한 Hint 설정
|
||||
memset( &hints, 0x00, sizeof(hints));
|
||||
hints.ai_family = PF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
// Thread Safe 한 DNS Resolve 처리함수 호출
|
||||
// int getaddrinfo(const char *hostname, const char *servname, const struct addrinfo *hints, struct addrinfo **res);
|
||||
|
||||
// CHG 2014-11-14 huibong
|
||||
// getaddrinfo 호출시 종종 EAI_NONAME 오류가 반환됨. (#20783)
|
||||
// - 따라서 오류 반환시 재시도하도록 기능 추가
|
||||
// CHG 2015-07-15 huibong
|
||||
// usleep 의 multi thread 상에서 block 발생 가능
|
||||
// - nanosleep 을 사용토록 변경 처리
|
||||
for( int count = 0 ; count < 3; count++ )
|
||||
{
|
||||
error = getaddrinfo( name, NULL, &hints, &result );
|
||||
|
||||
// 오류 발생시
|
||||
if( error != 0 )
|
||||
{
|
||||
if( result != NULL)
|
||||
{
|
||||
freeaddrinfo(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
// 잠시 대기 후 재시도 처리
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 정상 처리된 경우...
|
||||
// - loop 탈출
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 최종 오류 발생시
|
||||
if( error != 0 )
|
||||
{
|
||||
LOG( LERR, "name[%s] dns resolve fail. getaddrinfo return error [%d][%s]", name, error, gai_strerror(error) );
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
struct sockaddr_in * addr = (struct sockaddr_in *)result->ai_addr;
|
||||
retval = (unsigned int)(addr->sin_addr.s_addr);
|
||||
|
||||
// DNS Resovle 결과 확인용 코드
|
||||
/*
|
||||
struct addrinfo *temp;
|
||||
for( temp = result; temp; temp = temp->ai_next )
|
||||
{
|
||||
addr = (struct sockaddr_in *)temp->ai_addr;
|
||||
printf ("getaddrinfo result = %s\n",inet_ntoa( addr->sin_addr));
|
||||
}
|
||||
*/
|
||||
|
||||
// getaddrinfo() 함수에서 생성한 메모리 영역 해제 처리.
|
||||
freeaddrinfo(result);
|
||||
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/***************************************************************************
|
||||
BaseSocket ( Base Socket Class )
|
||||
-----------------------------------------
|
||||
begin : 2010/03/02
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2005 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 __LIBRARY_BASE_SOCKET_H__
|
||||
#define __LIBRARY_BASE_SOCKET_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#define SOCKET_NOT_VALID -1
|
||||
#define DEFAULT_DATA_RECEIVE_TIMEOUT 15 // sec
|
||||
|
||||
|
||||
class CBaseSocket
|
||||
{
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief socket descriptor
|
||||
int m_sock;
|
||||
|
||||
/// @brief socket 의 연결상태인지 여부를 저장하기 위한 변수.
|
||||
bool m_bConnected;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
/// @brief 생성자.
|
||||
CBaseSocket( const int & socket );
|
||||
|
||||
/// @brief 소멸자.
|
||||
~CBaseSocket();
|
||||
|
||||
/// @brief 소켓의 Close 처리를 수행함.
|
||||
void Close(void);
|
||||
|
||||
/// @brief 전달받은 Target 으로 Socket 접속을 수행
|
||||
/// @param szTarget [in] 접속 대상 Host name 또는 IP
|
||||
/// @param nPort [in] 접속 Port
|
||||
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
|
||||
bool Connect( const std::string& szTarget, int nPort );
|
||||
|
||||
/// @brief 멤버 변수인 m_sock 이 유효하고 연결된 상태인 경우 true 반환.
|
||||
/// @return socket이 유효하지 않거나 연결이 끊어지 경우 false 반환, 그외에는 true 반환.
|
||||
bool IsValidSocket(void);
|
||||
|
||||
/// @brief 멤버변수인 socket 의 접속 상태 여부를 설정하기 위한 함수.
|
||||
void SetConnectionStatus( bool bConnected) { m_bConnected = bConnected; }
|
||||
|
||||
/// @brief 현재 socket 의 접속 상태를 반환.
|
||||
bool GetConnectionStatus( void) { return m_bConnected; }
|
||||
|
||||
//void SetLogger( Logger * pLog ) { m_pLog = pLog; }
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief m_sock 로 부터 지정된 size 만큼 데이터 read 를 시도 ( read 함수와 동일 )
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t Read( void * vptr, size_t size );
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t ReadN( void * vptr, size_t size );
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
|
||||
ssize_t ReadNTimeout( void * vptr, size_t size, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
|
||||
|
||||
/// @brief m_sock 로부터 지정된 size 만큼 read 를 시도, \n
|
||||
///< 만약 지정된 크기만큼 데이터가 존재하지 않을 경우 해당 크기만큼 데이터를 read 할때까지 대기. \n
|
||||
///< 또는 지정된 Timeout 값 동안 read 를 하지 못하는 경우 오류 처리.
|
||||
///< 본 함수는 Socket 상에 이미 Data 가 존재하는 경우에만 사용.
|
||||
/// @param vptr [out] Read 된 데이터를 저장하기 위한 변수에 대한 포인터.
|
||||
/// @param size [in] read 하고자 하는 데이터의 크기.
|
||||
/// @param timeout [in] Timeout value (sec)
|
||||
/// @return read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
|
||||
ssize_t ReadNTimeout2( void * vptr, size_t size, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
|
||||
|
||||
public:
|
||||
/// @brief m_sock 으로 지정된 크기만큼 vptr 의 데이터를 전송 시도.\n
|
||||
///< Send Timeout 옵션 설정으로 Write Timeout 설정 가능. \n
|
||||
///< 지정된 횟수만큼 재전송 실패시 오류로 처리함.
|
||||
/// @param vptr [in] 전달할 데이터를 저장한 변수에 대한 포인터.
|
||||
/// @param size [in] write 하고자 하는 데이터의 크기.
|
||||
/// @return write 된 데이터의 크기. 0: fd closed, -1: 오류
|
||||
ssize_t WriteN( const void * vptr, size_t size );
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief 문자열로 전달받은 정보를 검사하여 IP 접속 정보를 생성한다.
|
||||
unsigned int ConversionAddr( const char * name );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __LIBRARY_BASE_SOCKET_H__ */
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <boost/algorithm/string.hpp> // use boost
|
||||
|
||||
|
||||
Config::Config()
|
||||
: m_keyDelimiter( CONFIG_DEFAULT_KEY_DELIMITER )
|
||||
, m_valueDelimiter( CONFIG_DEFAULT_VALUE_DELIMITER )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// @brief destructor
|
||||
Config::~Config()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
|
||||
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::Open( const string path )
|
||||
{
|
||||
ifstream file;
|
||||
|
||||
// Config file open
|
||||
file.open( path.c_str());
|
||||
if( file.is_open() == false )
|
||||
{
|
||||
cerr << "[error] " << __FILE__<< ":" << __func__ << ": config file open failed.[" << path << "][" << strerror( errno ) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 기존 데이터가 존재하면 삭제 처리.
|
||||
if( m_configData.empty() == false )
|
||||
m_configData.clear();
|
||||
|
||||
// config file parsing
|
||||
string line;
|
||||
string section;
|
||||
vector< string > configs;
|
||||
|
||||
// 루프를 돌면서 Config 파일을 line 단위로 읽어들인당....
|
||||
while( std::getline( file, line ) )
|
||||
{
|
||||
// 앞뒤 공백 제거 처리
|
||||
boost::trim( line );
|
||||
|
||||
// 공백 or 주석처리 라인 검사
|
||||
if( line.empty() == true || boost::starts_with( line, string("#") ) == true || line == "\r" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Section 여부 검사
|
||||
if( boost::starts_with( line, string("[") ) == true && boost::ends_with( line, string("]") ) == true )
|
||||
{
|
||||
// [] 로 정의된 section 의 문자열 값 추출
|
||||
line.erase( line.begin() );
|
||||
line.erase( line.end() -1 );
|
||||
boost::trim( line );
|
||||
|
||||
if( section.empty() == false && section != line )
|
||||
{
|
||||
// 이전 세션과 다른 신규 세션인 경우
|
||||
// 기존까지 저장했던 데이터를 멤버 변수에 입력 처리 후 내부변수 초기화 처리.
|
||||
|
||||
m_configData.insert( make_pair( section, configs ) );
|
||||
section.clear();
|
||||
configs.clear();
|
||||
}
|
||||
|
||||
// 신규 section 정보 저장처리.
|
||||
section = line;
|
||||
line.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Section 정보가 아닌 경우 => 실제 설정값 .. ^^
|
||||
configs.push_back( line );
|
||||
}
|
||||
}
|
||||
|
||||
// 마지막 세션 처리된 정보가 존재하는 경우 멤버 변수에 저장 처리.
|
||||
if( section.empty() == false )
|
||||
{
|
||||
m_configData.insert( make_pair( section, configs ) );
|
||||
}
|
||||
|
||||
// 종료 처리.
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
|
||||
/// @return void
|
||||
void Config::Clear()
|
||||
{
|
||||
m_configData.clear();
|
||||
}
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::GetConfig( const string section, const string key, string& value )
|
||||
{
|
||||
// Section 에 대한 임시 데이터 저장객체 생성
|
||||
vector< string > configs;
|
||||
|
||||
// 해당 Section 이 존재하지 않는 경우
|
||||
if( Find( section, configs ) == false )
|
||||
return false;
|
||||
|
||||
string line;
|
||||
string result;
|
||||
vector< string >::const_iterator it;
|
||||
|
||||
// 해당 Key 이 존재하는지 검사
|
||||
for( it = configs.begin(); it != configs.end(); it++)
|
||||
{
|
||||
line = *it;
|
||||
|
||||
// Line 상의 주석 제거
|
||||
string::size_type pos = line.find( '#' );
|
||||
if( pos != string::npos)
|
||||
{
|
||||
line = line.substr(0, pos);
|
||||
boost::trim( line );
|
||||
}
|
||||
|
||||
// key = value 에서 key 부분 추출
|
||||
pos = line.find( m_keyDelimiter );
|
||||
if( pos == string::npos )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = line.substr(0, pos);
|
||||
boost::trim( result );
|
||||
}
|
||||
|
||||
if( key == result )
|
||||
{
|
||||
// Key 값이 동일한 경우
|
||||
value = line.substr( pos+1 );
|
||||
boost::trim( value );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::GetConfig( const string section, const string key, vector< string >& value )
|
||||
{
|
||||
string result;
|
||||
vector< string > vec;
|
||||
|
||||
if( GetConfig( section, key, result ) == false )
|
||||
return false;
|
||||
|
||||
// 전달받은 result 값을 value delimiter 를 이용하여 parsing 처리
|
||||
boost::split( vec, result, boost::is_any_of( m_valueDelimiter ));
|
||||
if( vec.empty() == true )
|
||||
return false;
|
||||
|
||||
// 루프를 돌면서 trim 처리 후 결과값 저장처리.
|
||||
vector< string >::const_iterator it;
|
||||
for( it = vec.begin(); it != vec.end(); it++)
|
||||
{
|
||||
result = boost::trim_copy( *it );
|
||||
|
||||
if( result.empty() == false )
|
||||
value.push_back( result );
|
||||
}
|
||||
|
||||
if( value.empty() == true )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
|
||||
/// @param section [in] section 명
|
||||
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::Find( const string& section, vector<string>& configData )
|
||||
{
|
||||
map< string, vector< string > >::iterator it = m_configData.find( section );
|
||||
if( it != m_configData.end() )
|
||||
{
|
||||
// 해당 section 을 찾은 경우
|
||||
configData = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 해당 section 을 찾지 못한 경우
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/***************************************************************************
|
||||
Config File Parser Class
|
||||
-----------------------------------------
|
||||
begin : 2010/02/27
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2005 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 __LIBRARY_CONFIG_H__
|
||||
#define __LIBRARY_CONFIG_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#define CONFIG_DEFAULT_KEY_DELIMITER "="
|
||||
#define CONFIG_DEFAULT_VALUE_DELIMITER ","
|
||||
|
||||
|
||||
/// @brief Config class
|
||||
/// 1. config 파일의 내용을 파싱처리하여 각 Section, Key 에 해당하는 값을 반환처리
|
||||
/// 2. config 파일은 [section] 단위로 구분된다.
|
||||
/// 3. config 파일은 key=value 로 구분가능하며 이는 Delimiter 설정을 통해 변경가능하다.
|
||||
/// 4. value 값이 다중으로 존재하는 경우 "," 값을 통해 구분가능, Delimiter 변경시 다른 값도 사용가능함.
|
||||
/// 5. config 파일상에서 "#" 로 시작하는 문자열은 라인 끝까지 주석으로 처리된다.
|
||||
/// 6. 특정 Key 값에 대한 다중 value 값 조회는 vector<string> 을 통해 수행한다.
|
||||
/// 7. 만약 다중 value 값이 존재시 string 으로 반환받을 경우 해당 Row 가 통째로 반환된다.
|
||||
/// 8. 본 객체는 Open , Clear 함수가 호출되기전까지 이전 Config 정보가 저장된다.
|
||||
class Config
|
||||
{
|
||||
private:
|
||||
/// @brief config information saved variable
|
||||
// string : section data
|
||||
// vector<string> : key=value data
|
||||
map< string, vector< string> > m_configData;
|
||||
|
||||
/// @brief Key, Value 구분자 저장 변수
|
||||
string m_keyDelimiter;
|
||||
string m_valueDelimiter;
|
||||
|
||||
public:
|
||||
/// @brief constructor
|
||||
Config();
|
||||
|
||||
/// @brief destructor
|
||||
~Config();
|
||||
|
||||
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
|
||||
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Open( const string path );
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
|
||||
/// @return void
|
||||
void Clear();
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
|
||||
/// @return On success return true, otherwise return false
|
||||
bool GetConfig( const string section, const string key, string& value );
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool GetConfig( const string section, const string key, vector< string >& value );
|
||||
|
||||
/// @brief Key, Value Delimiter Get 함수
|
||||
/// @param key [out] key delimiter 값
|
||||
/// @param value [out] value delimiter 값
|
||||
void GetDelimiter( string& key, string& value )
|
||||
{
|
||||
key = m_keyDelimiter;
|
||||
value = m_valueDelimiter;
|
||||
}
|
||||
|
||||
// BUG 2010-12-06 huibong
|
||||
// string& value = CONFIG_DEFAULT_VALUE_DELIMITER 값은 문법상 오류 구문임.
|
||||
// gcc 3.4.6 버전에서는 Compile 되나 gcc 4.4.5 에서는 error 로 처리되어 수정처리함.
|
||||
|
||||
/// @brief Key, Value Delimiter Set 함수
|
||||
/// @param key [out] key delimiter 값
|
||||
/// @param value [out] value delimiter 값, 지정하지 않을 경우 Default 값이 사용됨.
|
||||
void SetDelimiter( string& key, string value = CONFIG_DEFAULT_VALUE_DELIMITER )
|
||||
{
|
||||
m_keyDelimiter = key;
|
||||
m_valueDelimiter = value;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
|
||||
/// @param section [in] section 명
|
||||
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Find( const string& section, vector<string>& configData );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* __LIBRARY_CONFIG_H__ */
|
||||
@@ -0,0 +1,110 @@
|
||||
/***************************************************************************
|
||||
Fixed size Queue Class ( FixedQueue.cpp )
|
||||
-----------------------------------------
|
||||
begin : 2013/08/09
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Development 1 Team
|
||||
email : dev1@solbox.com
|
||||
version : 3.2
|
||||
|
||||
CopyRight(C) 2005 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 "FixedQueue.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
CMutexLock::CMutexLock(pthread_mutex_t * mutex)
|
||||
: m_plock(mutex)
|
||||
{
|
||||
pthread_mutex_lock(m_plock);
|
||||
}
|
||||
|
||||
CMutexLock::~CMutexLock()
|
||||
{
|
||||
pthread_mutex_unlock(m_plock);
|
||||
}
|
||||
|
||||
CFixedQueue::CFixedQueue(int bufferSize, int qSzie )
|
||||
: m_bufferSize(bufferSize), m_queueSize(qSzie)
|
||||
{
|
||||
pthread_mutex_init(&m_mutex, NULL);
|
||||
}
|
||||
|
||||
CFixedQueue::~CFixedQueue()
|
||||
{
|
||||
ReleaseAll();
|
||||
pthread_mutex_destroy(&m_mutex);
|
||||
}
|
||||
|
||||
void CFixedQueue::ReleaseAll()
|
||||
{
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
|
||||
while (!m_queue.empty())
|
||||
{
|
||||
FixedQueueData *d = m_queue.front();
|
||||
if(d) delete (FixedQueueData*) d;
|
||||
m_queue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
int CFixedQueue::Push(char* buf, size_t buffersize)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
if(buf == NULL)
|
||||
return -1;
|
||||
|
||||
if(buffersize > (size_t) m_bufferSize)
|
||||
return -2;
|
||||
|
||||
CMutexLock lock (&m_mutex);
|
||||
do
|
||||
{
|
||||
if( m_queue.size() >= std::queue<char*>::size_type(m_queueSize) )
|
||||
{
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
FixedQueueData *d = new FixedQueueData;
|
||||
d->data_size = buffersize;
|
||||
d->data = new char [buffersize];
|
||||
memcpy(d->data, buf, buffersize);
|
||||
m_queue.push(d);
|
||||
r = 0;
|
||||
|
||||
}while (false);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int CFixedQueue::Pop(char* buf, size_t buffersize)
|
||||
{
|
||||
int r = 0;
|
||||
if (buf == NULL)
|
||||
return -1;
|
||||
|
||||
memset(buf, 0, buffersize);
|
||||
CMutexLock lock (&m_mutex);
|
||||
do
|
||||
{
|
||||
if( m_queue.empty())
|
||||
{
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
FixedQueueData *t = m_queue.front();
|
||||
memcpy(buf, t->data, t->data_size);
|
||||
delete (FixedQueueData *) t;
|
||||
m_queue.pop();
|
||||
r = 0;
|
||||
|
||||
} while (false);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/***************************************************************************
|
||||
Fixed size Queue Class Header ( FixedQueue.h )
|
||||
-----------------------------------------
|
||||
begin : 2013/08/09
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Development 1 Team
|
||||
email : dev1@solbox.com
|
||||
version : 3.5
|
||||
|
||||
CopyRight(C) 2005 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 __FIXED_SIZE_QUEUE_H__
|
||||
#define __FIXED_SIZE_QUEUE_H__
|
||||
|
||||
#include <pthread.h>
|
||||
#include <queue>
|
||||
|
||||
class CMutexLock
|
||||
{
|
||||
public:
|
||||
CMutexLock(pthread_mutex_t * mutex);
|
||||
~CMutexLock();
|
||||
|
||||
private:
|
||||
pthread_mutex_t* m_plock;
|
||||
};
|
||||
|
||||
|
||||
class FixedQueueData
|
||||
{
|
||||
public:
|
||||
size_t data_size;
|
||||
char * data;
|
||||
|
||||
FixedQueueData ()
|
||||
: data_size(0), data(NULL) {};
|
||||
|
||||
~FixedQueueData () { if(data) delete [] data; }
|
||||
};
|
||||
|
||||
class CFixedQueue
|
||||
{
|
||||
public:
|
||||
///@brief 생성자.
|
||||
///@param bufferSize [in] 데이터 버퍼 크기
|
||||
///@param qSzie [in] 큐의 크기
|
||||
CFixedQueue (int bufferSize, int qSzie );
|
||||
|
||||
///@brief 소멸자.
|
||||
~CFixedQueue();
|
||||
|
||||
///@brief 데이터 삽입
|
||||
///@param buf [in] 데이터
|
||||
///@param buffersize [in] 데이터 크기
|
||||
///@return 0: 정상, 1:queue full, -1 : error
|
||||
int Push(char* buf, size_t buffersize);
|
||||
|
||||
///@brief 데이터 추출
|
||||
///@return 데이터 리턴(FIFO)
|
||||
///@param buf [in] 데이터
|
||||
///@param buffersize [in] 데이터 크기
|
||||
///@return 0: 정상, 1: queue empty 나머지 : error
|
||||
int Pop(char* buf, size_t buffersize);
|
||||
|
||||
|
||||
protected:
|
||||
///@brief 큐에 초기화
|
||||
void ReleaseAll();
|
||||
|
||||
private:
|
||||
int m_bufferSize;
|
||||
int m_queueSize;
|
||||
|
||||
pthread_mutex_t m_mutex;
|
||||
|
||||
std::queue<FixedQueueData *> m_queue;
|
||||
};
|
||||
|
||||
#endif // __FIXED_SIZE_QUEUE_H__
|
||||
@@ -0,0 +1,487 @@
|
||||
/***************************************************************************
|
||||
Logger.cpp
|
||||
-----------------------------------------
|
||||
begin : 2011/10/26
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2005 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 "Logger.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
|
||||
#define DEFAULT_LOG_LEVEL LINF
|
||||
|
||||
using namespace std;
|
||||
|
||||
CLogger* CLogger::m_pInstance = NULL;
|
||||
bool CLogger::m_bIsInitialized = false;
|
||||
pthread_mutex_t CLogger::m_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
CLogger::CLogger( string programName, string logDir, int logLevel )
|
||||
{
|
||||
m_szProgramName = programName;
|
||||
m_szLogDir = logDir;
|
||||
|
||||
if( IsValidLogLevel( logLevel ) == true )
|
||||
{
|
||||
m_nLogLevel = logLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nLogLevel = DEFAULT_LOG_LEVEL;
|
||||
}
|
||||
|
||||
MakeLogLevelString();
|
||||
}
|
||||
|
||||
void CLogger::MakeLogLevelString()
|
||||
{
|
||||
m_vectorLogLevelString.push_back( "EMR" );
|
||||
m_vectorLogLevelString.push_back( "ALT" );
|
||||
m_vectorLogLevelString.push_back( "CRT" );
|
||||
m_vectorLogLevelString.push_back( "ERR" );
|
||||
m_vectorLogLevelString.push_back( "WAR" );
|
||||
m_vectorLogLevelString.push_back( "NOT" );
|
||||
m_vectorLogLevelString.push_back( "INF" );
|
||||
m_vectorLogLevelString.push_back( "DBG" );
|
||||
m_vectorLogLevelString.push_back( "DEV" );
|
||||
m_vectorLogLevelString.push_back( "DEV1" );
|
||||
m_vectorLogLevelString.push_back( "DEV2" );
|
||||
}
|
||||
|
||||
CLogger::~CLogger()
|
||||
{
|
||||
m_vectorLogLevelString.clear();
|
||||
}
|
||||
|
||||
bool CLogger::Init( string programName, string logDir, int logLevel )
|
||||
{
|
||||
if( m_bIsInitialized == true )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &m_mutex );
|
||||
|
||||
// 변수 유효성 검사.
|
||||
if( programName.empty() == true || logDir.empty() == true )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( MakeLogDir( programName, logDir ) == false )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( CLogger::m_pInstance != NULL )
|
||||
{
|
||||
delete CLogger::m_pInstance;
|
||||
CLogger::m_pInstance = NULL;
|
||||
}
|
||||
|
||||
CLogger::m_pInstance = new CLogger( programName, logDir, logLevel );
|
||||
m_bIsInitialized = true;
|
||||
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::IsValidLogLevel( int logLevel )
|
||||
{
|
||||
return ( ( logLevel < 0 || logLevel > MAX_LOG_LEVEL ) ? false : true );
|
||||
}
|
||||
|
||||
bool CLogger::SetLogLevel( int logLevel )
|
||||
{
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nLogLevel = logLevel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::MakeLogDir( string programName, string logDir )
|
||||
{
|
||||
string path = logDir + "/" + programName;
|
||||
|
||||
struct stat dirStat;
|
||||
|
||||
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
|
||||
if( lstat( path.c_str(), &dirStat ) == 0 )
|
||||
{
|
||||
// 해당 이름이 디렉토리인 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == true )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "exist file with the same name as log path(" << path << ")." << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 디렉토리 가 존재하지 않는 경우 디렉토리 생성 시도
|
||||
string cmd = "mkdir -p " + path;
|
||||
system( cmd.c_str() );
|
||||
|
||||
if( IsDirectory( path ) == false )
|
||||
{
|
||||
cerr << "can't make log directory. path=" << path << "." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::IsDirectory( string path )
|
||||
{
|
||||
struct stat dirStat;
|
||||
|
||||
if( lstat ( path.c_str(), &dirStat ) != 0 )
|
||||
{
|
||||
cerr << "Log path not valid. Check path [" << path << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 해당 정보가 Directory 가 아닌 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CLogger::Exit()
|
||||
{
|
||||
if( m_bIsInitialized == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &m_mutex );
|
||||
|
||||
if( CLogger::m_pInstance != NULL )
|
||||
{
|
||||
delete CLogger::m_pInstance;
|
||||
CLogger::m_pInstance = NULL;
|
||||
m_bIsInitialized = false;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
}
|
||||
|
||||
CLogger* CLogger::GetInstance()
|
||||
{
|
||||
return CLogger::m_pInstance;
|
||||
}
|
||||
|
||||
string CLogger::GetLogFilename( struct tm &timeNow )
|
||||
{
|
||||
char timeStr[256];
|
||||
snprintf( timeStr, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
|
||||
|
||||
// Make File Name
|
||||
string filename = m_szLogDir + "/" + m_szProgramName + "/" + m_szProgramName + "_" + string( timeStr );
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
bool CLogger::Write( int logLevel, const char * fmt, ...)
|
||||
{
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return Write( logLevel, PREFIX_DATE, NULL, 0, buffer );
|
||||
}
|
||||
|
||||
bool CLogger::WriteNoPrefix( int logLevel, const char * fmt, ...)
|
||||
{
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return Write( logLevel, PREFIX_NONE, NULL, 0, buffer );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// CHG 2012-08-16 huibong
|
||||
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
|
||||
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
|
||||
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
|
||||
// 함수명 : Write -> WriteWithFunc 으로 변경 처리
|
||||
bool CLogger::WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
|
||||
{
|
||||
if( filename == NULL || funcname == NULL || lineNum < 0 || fmt == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
|
||||
char className[255];
|
||||
string funcPrefix = GetClassName( className, filename );
|
||||
funcPrefix += "::" + string( funcname ) + "()";
|
||||
|
||||
return Write( logLevel, PREFIX_FUNCTION, funcPrefix.c_str(), lineNum, buffer );
|
||||
}
|
||||
|
||||
const char* CLogger::GetClassName( char* className, const char* filename )
|
||||
{
|
||||
if( className == NULL )
|
||||
{
|
||||
cout << "[ERROR] The input argument 'className' is NULL." << endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( filename == NULL )
|
||||
{
|
||||
cout << "[ERROR] The input argument 'filename' is NULL." << endl;
|
||||
strcpy( className, "NULL" );
|
||||
return className;
|
||||
}
|
||||
|
||||
int filenameSize = strlen( filename );
|
||||
memcpy( className, filename, filenameSize );
|
||||
|
||||
// find end position
|
||||
char endCharacter = '.';
|
||||
int endPos = filenameSize - 1;
|
||||
for( ; endPos > 0; --endPos )
|
||||
{
|
||||
if( className[endPos] == endCharacter )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( endPos == 0 )
|
||||
{
|
||||
cout << "[ERROR] endPos=0" << endl;
|
||||
strcpy( className, "NULL" );
|
||||
return className;
|
||||
}
|
||||
|
||||
className[endPos] = '\0';
|
||||
|
||||
// find start position
|
||||
char startCharacter = '/';
|
||||
int startPos = endPos - 1;
|
||||
for( ; startPos > 0; --startPos )
|
||||
{
|
||||
if( className[startPos] == startCharacter )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( startPos != 0 )
|
||||
{
|
||||
startPos += 1;
|
||||
}
|
||||
|
||||
return (className + startPos );
|
||||
}
|
||||
|
||||
bool CLogger::Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log )
|
||||
{
|
||||
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
||||
if( logLevel > m_nLogLevel )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( log == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Current Data & Time
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
string filename = GetLogFilename( timeNow );
|
||||
|
||||
// Log file open
|
||||
FILE* pFile = NULL;
|
||||
pFile = fopen( filename.c_str(), "a+" );
|
||||
|
||||
if( pFile == NULL )
|
||||
{
|
||||
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 시간 정보처리
|
||||
char timeStr[256];
|
||||
snprintf( timeStr, (size_t)256, "[%02d:%02d:%02d]"
|
||||
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
|
||||
|
||||
// Log Level 문자열 검색
|
||||
string szLevel = m_vectorLogLevelString[ logLevel ];
|
||||
|
||||
switch( logPrefix )
|
||||
{
|
||||
case PREFIX_DATE:
|
||||
/// 형식 예: [15:47:41] [DBG] sample log message.
|
||||
fprintf( pFile, "%s [%-4s] %s\n", timeStr, szLevel.c_str(), log );
|
||||
break;
|
||||
|
||||
case PREFIX_FUNCTION:
|
||||
if( functionName == NULL )
|
||||
{
|
||||
fclose( pFile );
|
||||
return false;
|
||||
}
|
||||
/// 형식 예: [15:47:41] [DBG] sample log message. [SomeClass::SomeFunction():12]
|
||||
fprintf( pFile, "%s [%-4s] %s [%s:%d]\n", timeStr, szLevel.c_str(), log, functionName, lineNum );
|
||||
break;
|
||||
|
||||
case PREFIX_NONE:
|
||||
/// 형식 예: sample log message.
|
||||
fprintf( pFile, "%s\n", log );
|
||||
break;
|
||||
}
|
||||
// Write to log file
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::WriteHex( int logLevel, const unsigned char* data, const int size )
|
||||
{
|
||||
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
||||
if( logLevel > m_nLogLevel )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( data == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( size <= 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Current Data & Time
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
string filename = GetLogFilename( timeNow );
|
||||
|
||||
// Log file open
|
||||
FILE* pFile = NULL;
|
||||
pFile = fopen( filename.c_str(), "a+" );
|
||||
|
||||
if( pFile == NULL )
|
||||
{
|
||||
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i = 0; i < size; ++i )
|
||||
{
|
||||
fprintf( pFile, "%02X", data[i] );
|
||||
|
||||
if( i == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ( (i+1) % 8 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, " " );
|
||||
}
|
||||
|
||||
if( ( (i+1) % 16 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, " " );
|
||||
}
|
||||
|
||||
if( ( (i+1) % 32 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, "\n" );
|
||||
}
|
||||
}
|
||||
fprintf( pFile, "\n" );
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/***************************************************************************
|
||||
Logger.h
|
||||
-----------------------------------------
|
||||
begin : 2011/10/26
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 3.2.0.805
|
||||
|
||||
CopyRight(C) 2005 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 __LOGGER_H__
|
||||
#define __LOGGER_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <pthread.h>
|
||||
|
||||
///@brief 로그 레벨 정의
|
||||
#define LEMR 0 /* system is or will be unusable if situation is not resolved */
|
||||
#define LALT 1 /* immediate action required */
|
||||
#define LCRT 2 /* critical situations */
|
||||
#define LERR 3 /* error conditions */
|
||||
#define LWAR 4 /* recoverable errors */
|
||||
#define LNOT 5 /* unusual situation that merits investigation */
|
||||
#define LINF 6 /* information messages */
|
||||
#define LDBG 7 /* verbose data for debugging */
|
||||
#define LDEV 8 /* verbose data for developer */
|
||||
#define LDEV1 9 /* start or end the function of application level */
|
||||
#define LDEV2 10 /* start or end the function of application level */
|
||||
|
||||
#define MAX_LOG_LEVEL LDEV2
|
||||
|
||||
|
||||
///@brief Log 정보를 파일로 저장하기 위한 Class.
|
||||
///1. 가변 format 으로 전달된 로그 관련 정보를 Log Level 에 따라 로그 파일에 아래의 4가지 형식으로 저장한다.
|
||||
///
|
||||
/// 1.1 Prefix로 [hh:mm:ss]와 [ClassName::FunctionName]이 추가된 로그
|
||||
/// LOG( level, format, ... ) 매크로 사용.
|
||||
/// 예) [17:43:40] [DBG] log level debug [LoggerTestTestLogger]
|
||||
///
|
||||
/// 1.2 Prefix로 [hh:mm:ss]이 추가된 로그
|
||||
/// _LOG( level, format, ... ) 매크로 사용
|
||||
/// 예) [17:43:40] [EMR] log level emergency
|
||||
///
|
||||
/// 1.3 Prefix가 없는 로그
|
||||
/// _LOG_( level, format, ... ) 매크로 사용
|
||||
/// 예) log level emergency
|
||||
///
|
||||
/// 1.4 Hex 로그.
|
||||
/// LOG_HEX( level, data, size ) 매크로 사용
|
||||
/// 예) 00010203 04050607 08090A0B 0C0D0E0F
|
||||
///
|
||||
///2. Logger 객체 초기화시 전달된 Log Level 정보보다 전달받은 Log Level 정보가 큰 경우 해당 로그는 파일로 저장되지 않는다.
|
||||
///
|
||||
///3. 로그 파일은 매 일단위로 저장파일이 변경된다.
|
||||
///
|
||||
///4. 로그 저장을 위한 program 경로가 존재하지 않는 경우 자동 생성 처리된다.
|
||||
///
|
||||
///5. 로그 저장방식은 매 저장로그마다 open-close 로 처리된다.
|
||||
///
|
||||
///6. 파일로 기록시 Log Level 에 대한 정보도 함께 기록된다.
|
||||
///
|
||||
///7. 싱글톤으로 작성되었고, CLogger::Init(...)시에 쓰레드 안정성을 제공한다.
|
||||
///
|
||||
///8. 동적으로 로그 레벨을 변경할 수 있는 인터페이스를 제공한다.
|
||||
///
|
||||
class CLogger
|
||||
{
|
||||
// Attributes
|
||||
private:
|
||||
///@brief 싱글톤 객체 인트턴스.
|
||||
static CLogger* m_pInstance;
|
||||
|
||||
///@brief 싱글톤 객체 초기화 여부.
|
||||
static bool m_bIsInitialized;
|
||||
|
||||
///@brief 싱글톤 객체 초기화시 스레드 안정성을 위한 뮤텍스.
|
||||
static pthread_mutex_t m_mutex;
|
||||
|
||||
///@brief 프로그램 이름. 로그 파일 경로를 만들 때 사용.
|
||||
std::string m_szProgramName;
|
||||
|
||||
///@brief 공통 로그 디렉토리 이름. 로그 파일 경로를 만들 때 사용.
|
||||
std::string m_szLogDir;
|
||||
|
||||
///@brief 로그 레벨.
|
||||
int m_nLogLevel;
|
||||
|
||||
///@brief 로그 레벨에 대응되는 문자열 정보를 저장.
|
||||
std::vector<std::string> m_vectorLogLevelString;
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
///@brief 로그 형식을 지정.
|
||||
typedef enum
|
||||
{
|
||||
PREFIX_NONE = 0, /// 클래스 설명의 1.3에 해당
|
||||
PREFIX_DATE, /// 클래스 설명의 1.2에 해당
|
||||
PREFIX_FUNCTION, /// 클래스 설명의 1.1에 해당
|
||||
} LOG_PREFIX;
|
||||
|
||||
// Operations
|
||||
private:
|
||||
///@brief 생성자.
|
||||
/// 프로그램 이름, 공통 로그 디렉토리, 로그 레벨을 저장하고
|
||||
/// 로그 레벨에 대응되는 문자열 정보를 만든다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@param logLevel [in] 로그 레벨
|
||||
CLogger( std::string programName, std::string logDir, int logLevel );
|
||||
|
||||
///@brief 소멸자.
|
||||
/// 로그 레벨에 대응되는 문자열 정보를 저장하고 있는
|
||||
/// 벡터 m_vectorLogLevelString을 clear 시킴.
|
||||
virtual ~CLogger();
|
||||
|
||||
///@brief 로그 레벨에 대응되는 문자열을 만든다.
|
||||
///@param none.
|
||||
///@return none.
|
||||
void MakeLogLevelString();
|
||||
|
||||
///@brief 로그 파일이 위치할 실제 로그 디렉토리를 생성한다.
|
||||
/// 생성할 디렉토리 경로는 'logDir/programName'이 된다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@return 디렉토리가 이미 존재하거나 생성 성공하면 true,
|
||||
/// 해당 경로가 존재하지만 디렉토리가 아니거나, 디렉토리 생성 실패하면 false 반환.
|
||||
static bool MakeLogDir( std::string programName, std::string logDir );
|
||||
|
||||
///@brief 해당 경로가 디렉토리 인지 아닌지 판단.
|
||||
///@param path [in] 디렉토리 인지 아닌지 판단할 경로.
|
||||
///@return 해당 경로가 디렉토이면 true,
|
||||
/// 경로가 존재하지 않거나 디렉토리가 아니면 false 반환.
|
||||
static bool IsDirectory( std::string path );
|
||||
|
||||
///@brief 로그 레벨이 올바른지 판별.
|
||||
///@param logLevel [in]
|
||||
///@return 올바른 로그 레벨이면 true, 그렇지 않으면 false 반환.
|
||||
static bool IsValidLogLevel( int logLevel );
|
||||
|
||||
///@brief 시간 정보를 입력 받아 로그 파일 이름을 만든다.
|
||||
/// 로그 파일 이름 형식 : 프로그램명_YYYYMMDD.log
|
||||
///param timeNow [in] 현재 시간 정보.
|
||||
///return 로그 파일 이름.
|
||||
std::string GetLogFilename( struct tm &timeNow );
|
||||
|
||||
///@brief 로그를 남기는는 클래스가 정의된 파일의 이름에서 클래스명을 추출한다.
|
||||
/// 쓰레드 안정성을 보장한다.
|
||||
///@param className [out] 파일 이름에서 추출된 클래스명
|
||||
///@param filename [in] 파일 이름.
|
||||
///@return 클래스명 추출이 성공하면 클래스명 문자열의 포인터, 추출 실패하면 NULL.
|
||||
const char* GetClassName( char* className, const char* filename );
|
||||
|
||||
///@brief 인자 logPrefix에 따라 적절한 형식으로 로그 파일에 로그를 저장한다.
|
||||
/// 인자 logLevel이 설정된 로그 레벨보다 높으면 로그를 출력하지 않는다.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param logPrefix [in] 로그 프리픽스 종류.
|
||||
///@functionName [in] ClassName::FunctionNmae() 형식의 문자열.
|
||||
///@lineNum [in] 라인 번호.
|
||||
///@log [in] 출력하고자 하는 로그 내용.
|
||||
bool Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log );
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
///@brief 싱글톤 객체 m_pInstance를 생성하고 인자 정보로 로그 디렉토리를 만든다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@return 로그 디렉토리를 만들고 싱글톤 객체를 생성했으면 true,
|
||||
/// 로그 디렉토리를 만들지 못 했거나 인자 값이 올바르지 않으면 false 반환.
|
||||
static bool Init( std::string programName, std::string logDir, int logLevel );
|
||||
|
||||
///@brief 싱글톤 객체 m_pInstance를 delete 한다.
|
||||
///@param none.
|
||||
///@return none.
|
||||
static void Exit();
|
||||
|
||||
///@brief 싱글톤 객체 m_pInstance를 반환한다.
|
||||
///@param none.
|
||||
///@return CLogger 객체의 인스턴스.
|
||||
static CLogger* GetInstance();
|
||||
|
||||
///@brief Prefix로 [hh:mm:ss]이 추가된 형식으로 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool Write( int logLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
///@brief Prefix가 없는 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteNoPrefix( int logLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
|
||||
// CHG 2012-08-16 huibong
|
||||
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
|
||||
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
|
||||
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
|
||||
|
||||
///@brief Prefix로 [hh:mm:ss]와 [ClassName::FunctionName:line]이 추가된 형식으로 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param filename [in] 파일 이름
|
||||
///@param funcname [in] 함수 이름
|
||||
///@param lineNum [in] 라인 번호
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
|
||||
__attribute__((format(printf, 6, 7)));
|
||||
|
||||
///@brief 로그를 hex 형식으로 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param data [in] hex 형식으로 출력할 데이터.
|
||||
///@pram size [in] data의 크기.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteHex( int logLevel, const unsigned char* data, const int size );
|
||||
|
||||
///@brief 로그을 설정한다. 인자 logLevel이 적절한 값이면 새로운 값으로 변경하고
|
||||
/// 적절한 값이 아니면 로그 레벨을 변경하지 않는다.
|
||||
///@param logLevel [in] 설정할 로그 레벨
|
||||
///@return none.
|
||||
bool SetLogLevel( int logLevel );
|
||||
|
||||
inline int GetLogLevel() { return m_nLogLevel; };
|
||||
inline std::string GetLogDir() { return m_szLogDir + "/" + m_szProgramName; };
|
||||
};
|
||||
|
||||
#define LOG( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteWithFunc( level, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->Write( level, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG_( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteNoPrefix( level, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG_HEX_( level, data, size ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteHex( level, (const unsigned char*)data, size ); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define FUNC_BEGIN() LOG( LDEV1, "begin" )
|
||||
#define FUNC_END() LOG( LDEV1, "end" )
|
||||
|
||||
#define FRM_BEGIN() LOG( LDEV2, "begin" )
|
||||
#define FRM_END() LOG( LDEV2, "end" )
|
||||
|
||||
#endif // __LOGGER_H__
|
||||
@@ -0,0 +1,68 @@
|
||||
#****************************************************************************
|
||||
# Makefile for Cloud Storage Common Libaray
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2013/06/04
|
||||
# copyright : (C) 2013 Solbox Inc.
|
||||
# author : Development 1 Team
|
||||
# email : dev1@solbox.com
|
||||
# version : 3.3.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.
|
||||
#*****************************************************************************
|
||||
|
||||
# Library info
|
||||
|
||||
LIB_NAME = InterCommon
|
||||
|
||||
LIB = lib$(LIB_NAME).a
|
||||
|
||||
OBJS = Config.o Logger.o BaseSocket.o FixedQueue.o ProcessRename.o ReportLog.o
|
||||
|
||||
# Compiler info
|
||||
CC = /usr/bin/g++
|
||||
AR = /usr/bin/ar
|
||||
|
||||
DIR_INCLUDE = -I/usr/local/include
|
||||
ifeq ($(DEBUG), yes)
|
||||
CFLAGS = -Wall -O0 -g -Wreturn-type -Wunused -Wuninitialized\
|
||||
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
|
||||
-D_REENTRANT -D_THREAD_SAFE -D_PTHREADS
|
||||
LFLAGS =
|
||||
DFLAGS =
|
||||
else
|
||||
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized\
|
||||
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
|
||||
-D_REENTRANT -D_THREAD_SAFE -D_PTHREADS
|
||||
LFLAGS =
|
||||
DFLAGS =
|
||||
endif
|
||||
############################
|
||||
|
||||
all:$(LIB)
|
||||
sync
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
|
||||
|
||||
|
||||
$(LIB): $(OBJS)
|
||||
$(AR) crsv $@ $^
|
||||
|
||||
|
||||
clean:
|
||||
-rm -f *.o core *.out *.log
|
||||
-rm -f $(LIB)
|
||||
sync
|
||||
|
||||
|
||||
install :
|
||||
sync
|
||||
|
||||
|
||||
# End of Makefile
|
||||
@@ -0,0 +1,185 @@
|
||||
#include "ProcessRename.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
typedef size_t Size;
|
||||
#define LONG_ALIGN_MASK (sizeof(long) - 1)
|
||||
#define MEMSET_LOOP_LIMIT 1024
|
||||
#define MemSet(start, val, len) \
|
||||
do \
|
||||
{ \
|
||||
/* must be void* because we don't know if it is integer aligned yet */ \
|
||||
void *_vstart = (void *) (start); \
|
||||
int _val = (val); \
|
||||
Size _len = (len); \
|
||||
\
|
||||
if ((((long) _vstart) & LONG_ALIGN_MASK) == 0 && \
|
||||
(_len & LONG_ALIGN_MASK) == 0 && \
|
||||
_val == 0 && \
|
||||
_len <= MEMSET_LOOP_LIMIT && \
|
||||
/* \
|
||||
* If MEMSET_LOOP_LIMIT == 0, optimizer should find \
|
||||
* the whole "if" false at compile time. \
|
||||
*/ \
|
||||
MEMSET_LOOP_LIMIT != 0) \
|
||||
{ \
|
||||
long *_start = (long *) _vstart; \
|
||||
long *_stop = (long *) ((char *) _start + _len); \
|
||||
while (_start < _stop) \
|
||||
*_start++ = 0; \
|
||||
} \
|
||||
else \
|
||||
memset(_vstart, _val, _len); \
|
||||
} while (0)
|
||||
|
||||
#define PS_PADDING '\0'
|
||||
|
||||
extern char **environ;
|
||||
bool update_process_title = true;
|
||||
|
||||
static char *ps_buffer; /* will point to argv area */
|
||||
static size_t ps_buffer_size; /* space determined at run time */
|
||||
static size_t last_status_len; /* use to minimize length of clobber */
|
||||
static size_t ps_buffer_cur_len; /* nominal strlen(ps_buffer) */
|
||||
static size_t ps_buffer_fixed_size; /* size of the constant prefix */
|
||||
|
||||
static int save_argc;
|
||||
static char **save_argv;
|
||||
|
||||
size_t strlcpy(char *dst, const char *src, size_t siz)
|
||||
{
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = siz;
|
||||
|
||||
/* Copy as many bytes as will fit */
|
||||
if (n != 0)
|
||||
{
|
||||
while (--n != 0)
|
||||
{
|
||||
if ((*d++ = *s++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||
if (n == 0)
|
||||
{
|
||||
if (siz != 0)
|
||||
*d = '\0'; /* NUL-terminate dst */
|
||||
while (*s++)
|
||||
;
|
||||
}
|
||||
|
||||
return (s - src - 1); /* count does not include NUL */
|
||||
}
|
||||
|
||||
|
||||
void set_ps_display(const char *activity, bool force)
|
||||
{
|
||||
/* update_process_title=off disables updates, unless force = true */
|
||||
if (!force && !update_process_title)
|
||||
return;
|
||||
|
||||
/* no ps display for stand-alone backend */
|
||||
//if (!IsUnderPostmaster)
|
||||
// return;
|
||||
|
||||
/* If ps_buffer is a pointer, it might still be null */
|
||||
if (!ps_buffer)
|
||||
return;
|
||||
/* Update ps_buffer to contain both fixed part and activity */
|
||||
strlcpy(ps_buffer + ps_buffer_fixed_size, activity,
|
||||
ps_buffer_size - ps_buffer_fixed_size);
|
||||
ps_buffer_cur_len = strlen(ps_buffer);
|
||||
|
||||
/* pad unused memory; need only clobber remainder of old status string */
|
||||
if (last_status_len > ps_buffer_cur_len)
|
||||
MemSet(ps_buffer + ps_buffer_cur_len, PS_PADDING,
|
||||
last_status_len - ps_buffer_cur_len);
|
||||
last_status_len = ps_buffer_cur_len;
|
||||
|
||||
}
|
||||
|
||||
|
||||
char ** save_ps_display_args(int argc, char **argv)
|
||||
{
|
||||
save_argc = argc;
|
||||
save_argv = argv;
|
||||
|
||||
/*
|
||||
* If we're going to overwrite the argv area, count the available space.
|
||||
* Also move the environment to make additional room.
|
||||
*/
|
||||
{
|
||||
char *end_of_area = NULL;
|
||||
char **new_environ;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* check for contiguous argv strings
|
||||
*/
|
||||
for (i = 0; i < argc; i++)
|
||||
{
|
||||
if (i == 0 || end_of_area + 1 == argv[i])
|
||||
end_of_area = argv[i] + strlen(argv[i]);
|
||||
}
|
||||
|
||||
if (end_of_area == NULL) /* probably can't happen? */
|
||||
{
|
||||
ps_buffer = NULL;
|
||||
ps_buffer_size = 0;
|
||||
return argv;
|
||||
}
|
||||
|
||||
/*
|
||||
* check for contiguous environ strings following argv
|
||||
*/
|
||||
for (i = 0; environ[i] != NULL; i++)
|
||||
{
|
||||
if (end_of_area + 1 == environ[i])
|
||||
end_of_area = environ[i] + strlen(environ[i]);
|
||||
}
|
||||
|
||||
ps_buffer = argv[0];
|
||||
last_status_len = ps_buffer_size = end_of_area - argv[0];
|
||||
|
||||
/*
|
||||
* move the environment out of the way
|
||||
*/
|
||||
new_environ = (char **) malloc((i + 1) * sizeof(char *));
|
||||
for (i = 0; environ[i] != NULL; i++)
|
||||
new_environ[i] = strdup(environ[i]);
|
||||
new_environ[i] = NULL;
|
||||
environ = new_environ;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're going to change the original argv[] then make a copy for
|
||||
* argument parsing purposes.
|
||||
*
|
||||
* (NB: do NOT think to remove the copying of argv[], even though
|
||||
* postmaster.c finishes looking at argv[] long before we ever consider
|
||||
* changing the ps display. On some platforms, getopt() keeps pointers
|
||||
* into the argv array, and will get horribly confused when it is
|
||||
* re-called to analyze a subprocess' argument string if the argv storage
|
||||
* has been clobbered meanwhile. Other platforms have other dependencies
|
||||
* on argv[].
|
||||
*/
|
||||
{
|
||||
char **new_argv;
|
||||
int i;
|
||||
|
||||
new_argv = (char **) malloc((argc + 1) * sizeof(char *));
|
||||
for (i = 0; i < argc; i++)
|
||||
new_argv[i] = strdup(argv[i]);
|
||||
new_argv[argc] = NULL;
|
||||
|
||||
argv = new_argv;
|
||||
}
|
||||
|
||||
return argv;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/***************************************************************************
|
||||
Process rename functions
|
||||
-----------------------------------------
|
||||
begin : 2013/06/21
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev 1 Team
|
||||
email : dev1@solbox.com
|
||||
version : 3.2.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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __PROCESS_RENAME_H__
|
||||
#define __PROCESS_RENAME_H__
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief linux의 Process Title을 변경
|
||||
void set_ps_display(const char *activity, bool force);
|
||||
|
||||
/// @brief main에 argv의 실제 위치를 기억하고 새로은 메모리 활당하여 반환
|
||||
char ** save_ps_display_args(int argc, char **argv);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif // __PROCESS_RENAME_H__
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "ReportLog.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#define REPORT_LOG_NAME "report" // Report Log 폴더 및 파일 명칭 지정자..
|
||||
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
|
||||
|
||||
|
||||
// 생성자..
|
||||
CReportLog::CReportLog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// 소멸자..
|
||||
CReportLog::~CReportLog()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool CReportLog::Init( const char * szPath )
|
||||
{
|
||||
// 멤버 변수 초기화.
|
||||
m_strFullPath.clear();
|
||||
m_strLastError.clear();
|
||||
|
||||
// 변수 유효성 검사.
|
||||
if( szPath == NULL || strlen( szPath ) == 0 )
|
||||
{
|
||||
m_strLastError = "Log path empty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Path check...
|
||||
struct stat dirStat;
|
||||
if( lstat ( szPath, &dirStat ) != 0)
|
||||
{
|
||||
//m_strLastError = "Log path not valid. Check path [" + szPath + "][" + strerror(errno) + "]" ;
|
||||
m_strLastError = "Log path not valid. Check path [";
|
||||
m_strLastError.append( szPath );
|
||||
m_strLastError.append( "][" );
|
||||
m_strLastError.append( strerror(errno) );
|
||||
m_strLastError.append( "]" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 해당 정보가 Directory 가 아닌 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == 0 )
|
||||
{
|
||||
if( S_ISLNK( dirStat.st_mode ) == 0 )
|
||||
{
|
||||
//m_strLastError = "Log path not directory or link. Check path [" + szPath + "]";
|
||||
m_strLastError = "Log path not directory or link. Check path [" ;
|
||||
m_strLastError.append( szPath );
|
||||
m_strLastError.append ( "]" );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//m_strLastError = "Log path is symbolic link. path [" + szPath + "]";
|
||||
m_strLastError = "Log path is symbolic link. path [";
|
||||
m_strLastError.append( szPath );
|
||||
m_strLastError.append( "]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 멤버 변수 정보 설정.
|
||||
m_strFullPath = szPath;
|
||||
m_strFullPath.append( "/" );
|
||||
m_strFullPath.append( REPORT_LOG_NAME );
|
||||
|
||||
// 디렉토리 정보 검사
|
||||
if( lstat ( m_strFullPath.c_str(), &dirStat ) == 0)
|
||||
{
|
||||
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
|
||||
if( S_ISDIR( dirStat.st_mode ) != 0 )
|
||||
{
|
||||
// 해당 이름이 디렉토리인 경우
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 최종 경로에 대한 Directory 가 존재하지 않는 경우 Directory 생성 시도
|
||||
// 2013-11-27 report 로그는 fluentd 에 의해 감시, 분석되어야 하는데.. root 계정이 아닌 다른 td-agent 계정을 사용함.
|
||||
// 이로 인해 fluentd 에서 report 폴더에 접근할 수 있도록 폴더 권한 설정을 777 로 해야 함.
|
||||
if( mkdir( m_strFullPath.c_str(), 0777 ) != 0 )
|
||||
{
|
||||
m_strLastError = "Log directory create failed. Check path [" + m_strFullPath + "][" + strerror(errno) + "]";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CReportLog::Write( const char * szLevel, const char * fmt, ...)
|
||||
{
|
||||
// Get Current Data & Time
|
||||
char szTimeString[256];
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
// Make File Name
|
||||
std::string fileName = m_strFullPath + "/" + REPORT_LOG_NAME + "_";
|
||||
snprintf( szTimeString, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
|
||||
fileName.append( szTimeString );
|
||||
|
||||
|
||||
// Log file open
|
||||
FILE * pFile = NULL;
|
||||
pFile = fopen( fileName.c_str(), "a+" );
|
||||
if( pFile == NULL )
|
||||
{
|
||||
m_strLastError = "Log file open fail.[" + fileName + "][" + strerror(errno) + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 시간 정보처리
|
||||
snprintf( szTimeString, (size_t)256, "[%04d-%02d-%02d %02d:%02d:%02d]"
|
||||
, timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday
|
||||
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
|
||||
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char szBuffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( szBuffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
fclose( pFile );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
|
||||
// Write to log file
|
||||
fprintf( pFile, "%s [%s] %s\n", szTimeString, szLevel, szBuffer );
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
Report 로그 처리용 Class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2013/11/25
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev 1 Team
|
||||
email : dev1@solbox.com
|
||||
version : 3.2.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.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef __REPORT_LOG_H__
|
||||
#define __REPORT_LOG_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string>
|
||||
|
||||
|
||||
/// @brief CReportLog
|
||||
/// 본 클래스는 전달받은 경로 하위 report 폴더 상에...
|
||||
/// 운영자들에게 전달할 report 관련 log 를 기록하기 위한 클래스임.
|
||||
/// lib 에 존재하는 CLogger Class 가 프로세스에 대해 singleton 방식으로 동작하므로... 해당 Class 를 사용하기는 어려움.
|
||||
/// 따라서 report 관련 로그만 전담하여 처리하기 위한 로그 Class 를 추가함.
|
||||
class CReportLog
|
||||
{
|
||||
public:
|
||||
|
||||
// constructor
|
||||
CReportLog();
|
||||
|
||||
// destructor
|
||||
~CReportLog();
|
||||
|
||||
// 초기화..
|
||||
bool Init( const char * szPath );
|
||||
|
||||
// log wirte
|
||||
bool Write( const char * szLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
// Get last error log
|
||||
void GetLastError( std::string & errorMessage ) { errorMessage = m_strLastError; return; }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Log Full Path
|
||||
std::string m_strFullPath;
|
||||
|
||||
// last Error String
|
||||
std::string m_strLastError;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __REPORT_LOG_H__ */
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/***************************************************************************
|
||||
rc_mond shared memory template header
|
||||
-----------------------------------------
|
||||
begin : 2015/06/15
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Storage Dev 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.
|
||||
***************************************************************************/
|
||||
|
||||
// 본 코드는 시스템 공유메모리 생성, 접근, 해제, 삭제 처리를 수행하기 위한 코드로서
|
||||
// template 코드 방식으로 구성
|
||||
|
||||
#ifndef __SHM_CONTROL_H__
|
||||
#define __SHM_CONTROL_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/shm.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/*----------- MODE Flag ---------------
|
||||
SHM_R 0400 read by user
|
||||
SHM_W 0200 write by user
|
||||
SHM_R >> 3 0040 read by group
|
||||
SHM_W >> 3 0020 write by group
|
||||
SHM_R >> 6 0004 read by others
|
||||
SHM_W >> 6 0002 write by others
|
||||
-------------------------------------*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief ShmControl 공유메모리 관련 Template Class
|
||||
template <typename T>
|
||||
class CShmControl
|
||||
{
|
||||
public:
|
||||
/// @brief 생성자
|
||||
CShmControl();
|
||||
/// @brief 소멸자
|
||||
~CShmControl();
|
||||
|
||||
|
||||
/// @brief 공유메모리 생성, 접근 또는 기존 공유메모리 접근.
|
||||
/// @param iShmKey 공유메모리 접근을 위한 키
|
||||
/// @param iSize 접근하고자 하는 공유메모리의 크기
|
||||
/// @param iSize 공유메모리 생성및 접근시 권한
|
||||
/// @return return true or false
|
||||
bool Create( key_t iShmKey, size_t iSize, int iModeFlag = SHM_R | SHM_W );
|
||||
|
||||
/// @brief 기존 공유메모리 접근.
|
||||
/// @param iShmKey 공유메모리 접근을 위한 키
|
||||
/// @param iSize 접근하고자 하는 공유메모리의 크기
|
||||
/// @param iSize 공유메모리 접근시 권한
|
||||
/// @return return true or false
|
||||
bool Attach( key_t iShmKey, size_t iSize, int iModeFlag = SHM_R | SHM_W );
|
||||
|
||||
/// @brief 기존 접근한 공유메모리와의 연결을 해제
|
||||
/// @return return true or false
|
||||
bool Detach( void );
|
||||
|
||||
/// @brief 기존 접근한 공유메모리와의 연결을 해제 및 삭제
|
||||
/// @return return true or false
|
||||
bool Remove( void );
|
||||
|
||||
/// @brief 공유메모리에 대한 포인터를 반환
|
||||
/// @return 생성 및 접근한 공유메모리에 대한 포인터 또는 오류 발생시 NULL
|
||||
T * GetShmPtr(void) const { return m_pShm; }
|
||||
|
||||
/// @brief 접근한 공유메모리의 ID 값을 반환
|
||||
/// @return 생성 및 접근한 공유메모리 ID Value에 대한 참조.
|
||||
int GetShmId(void) const { return m_iShmId; }
|
||||
|
||||
private:
|
||||
///< m_pShm 공유메모리에 대한 포인터
|
||||
T * m_pShm;
|
||||
///< m_pShmId 접근한 공유메모리의 ID Value
|
||||
int m_iShmId;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 기본 멤버변수 초기화.
|
||||
template < typename T >
|
||||
inline CShmControl<T>::CShmControl()
|
||||
: m_pShm(NULL)
|
||||
, m_iShmId(-1)
|
||||
{
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
inline CShmControl<T>::~CShmControl()
|
||||
{
|
||||
// 생성된 공유메모리를 종료시 다른 프로세스들이 사용한다면.. 맘대로 삭제해서는 안되며...
|
||||
// 이럴 경우 detach 처리만 해야 한다.
|
||||
// Detach();
|
||||
|
||||
// 이와 반대로.. 다른 프로세스에서 더이상 해당 공유메모리를 사용하지 않을 경우.. remove 처리한다.
|
||||
Remove();
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 해당 공유메모리를 생성한다. 만약 기존에 생성된 것이 있다면 그놈을 사용하도록 한다.
|
||||
template < typename T >
|
||||
inline bool CShmControl<T>::Create( key_t iShmKey, size_t iSize, int iModeFlag )
|
||||
{
|
||||
// 우선 공유 메모리를 생성해 봅니다.
|
||||
m_iShmId = shmget( iShmKey, iSize, IPC_CREAT | IPC_EXCL | iModeFlag );
|
||||
// 생성에 실패한 경우.
|
||||
if( m_iShmId == -1)
|
||||
{
|
||||
// 이미 기존에 생성된 놈이 있는지 확인해 봅시다.
|
||||
return Attach( iShmKey, iSize, iModeFlag);
|
||||
}
|
||||
// 생성에 성공한 경우.
|
||||
else
|
||||
{
|
||||
// 생성된 공유 메모리의 포인터를 받아옵니다.
|
||||
m_pShm = (T *)shmat( m_iShmId, 0, 0 );
|
||||
|
||||
// 받아온 공유 메모리 포인터 값이 정확하면
|
||||
if( reinterpret_cast<long>(m_pShm) != -1)
|
||||
{
|
||||
// 원래 시스템에서 생성시 자동으로 초기화됨 => 그렇지만 새로이 생성을 하였으므로 확인 사살 초기화 ^^
|
||||
memset( m_pShm, 0x00, iSize);
|
||||
return true;
|
||||
}
|
||||
// 받아온 공유 메모리 포인터를 가져오는데 실패한 경우
|
||||
// => 만든놈이 삭제해 버립시다.
|
||||
else
|
||||
{
|
||||
Remove();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 기존에 생성된 것 공유 메모리에 접근하기 위해서 사용한다..
|
||||
// 키값과 크기값을 정확히 알고 있어야 ( 혹시 다른데서 사용하는 놈으로 붙을까봐 ) 접근할 수 있도록 한다.
|
||||
template < typename T >
|
||||
inline bool CShmControl<T>::Attach( key_t iShmKey, size_t iSize, int iModeFlag )
|
||||
{
|
||||
// 기존 연결은 해제 시켜 버린다. => 그냥 놓은 상태에서 다시 접속할 경우 메모리 사용량이 올라감.
|
||||
Detach();
|
||||
|
||||
m_iShmId = shmget( iShmKey, iSize, iModeFlag);
|
||||
if( m_iShmId == -1 )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
// 우선 기존에 있는 것과 크기가 동일한지 검사한다.
|
||||
struct shmid_ds shmInfo;
|
||||
memset(&shmInfo, 0x00, sizeof(struct shmid_ds));
|
||||
|
||||
shmctl( m_iShmId, IPC_STAT, &shmInfo);
|
||||
|
||||
// shm_segsz 정의 관련 참고사항
|
||||
// - FreeBSD 6.X : int type으로 정의되어.. 컴파일시 waring 발생
|
||||
// - FreeBSD 10.X : size_t type
|
||||
// - CentOS 6.4 : size_t type
|
||||
if( shmInfo.shm_segsz != iSize)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
|
||||
m_pShm = (T *)shmat( m_iShmId, 0, 0 );
|
||||
|
||||
// reinterpret_cast 을 사용한 것은 운영체제마다 포인터 형이 다를수가 있다고 참고한 라이브러리에 나와있어서 함.
|
||||
if( reinterpret_cast<long>(m_pShm) == -1)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 접근한 공유메모리로 부터 분리시킨다. ( 접근시 설정된 멤버 변수 이용)
|
||||
template < typename T >
|
||||
inline bool CShmControl<T>::Detach()
|
||||
{
|
||||
if( m_pShm > 0 )
|
||||
{
|
||||
if( shmdt( m_pShm) == 0)
|
||||
{
|
||||
m_iShmId = -1;
|
||||
m_pShm = NULL;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
// 공유메모리를 삭제한다. 이 작업을 하기 위해서는 먼저 공유 메모리를 생성 또는 접근을 해야하며
|
||||
// 이때 설정된 멤버 변수를 가지고 삭제 작업을 수행한다.
|
||||
// 공유 메모리는 여러 Process 들이 사용가능하기 때문에 삭제시 신중을 기할 것.
|
||||
template < typename T >
|
||||
inline bool CShmControl<T>::Remove()
|
||||
{
|
||||
if( m_iShmId > -1)
|
||||
{
|
||||
if( shmctl( m_iShmId, IPC_RMID, NULL) == -1)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_iShmId = -1;
|
||||
m_pShm = NULL;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif /* __SHM_CONTROL_H__ */
|
||||
@@ -0,0 +1,127 @@
|
||||
/****************************************************************************
|
||||
Shared memory define for rc_mond ( Revision 1440 )
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2015/06/09
|
||||
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 __SHM_DEFINE_H__
|
||||
#define __SHM_DEFINE_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
// hostname 최대 길이.
|
||||
#define HOST_NAME_LENGTH 63
|
||||
|
||||
// client ip 주소 저장을 위한 최대 크기값
|
||||
// - NULL 포함 40자까지, IPv6 지원 목적
|
||||
#define IP_ADDR_LENGTH 40
|
||||
|
||||
|
||||
// Service 상태 코드 정의
|
||||
#define SERVICE_STAT_OK 0 // 정상 상태
|
||||
#define SERVICE_STAT_OUT 1 // 수동 중지 상태
|
||||
#define SERVICE_STAT_DISABLE 2 // 서비스 불가 상태
|
||||
#define SERVICE_STAT_RONLY 3 // Read Only 상태
|
||||
|
||||
// ShmDefine.h 에서 정의된 서비스 상태 코드 외에 미정의된 코드 처리를 위한 정의
|
||||
#define SERVICE_STAT_NOT_DEFINE -1
|
||||
|
||||
// rc_monde src/Makefile 에 정의된 값
|
||||
#define MAX_CLIENT_COUNT 1024
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// 각 Client 로 부터 수신된 정보를 저장하기 위한 공유메모리 정의 파일
|
||||
// - 공유메모리 관련 상세 내역은 rc_mond 설계서 의 공유메모리 구조 항목을 참고할 것.
|
||||
|
||||
|
||||
// Service 상태 정보
|
||||
struct ServiceInfo {
|
||||
|
||||
time_t update_time; // 최종 update 시간 (local time, 시간동기화 영향 받음)
|
||||
int service_stat; // Service 상태 code. ( -1: 정보 미수신 상태, 그외 csagentd 설정과 동일)
|
||||
|
||||
time_t update_time_clock_sec; // clock_gettime(CLOCK_MONOTONIC) 을 통해 추출된 sec 정보. expire 여부 판단을 위해 사용.
|
||||
};
|
||||
|
||||
|
||||
// System 상태 정보
|
||||
struct SystemInfo {
|
||||
|
||||
time_t update_time; // 최종 update 시간
|
||||
|
||||
double load_avg[3]; // load average.
|
||||
|
||||
uint64_t memory_total; // Total memory size (byte 단위)
|
||||
uint64_t memory_free; // Free memory size (byte 단위)
|
||||
int memory_usage; // memory 사용률 (%)
|
||||
|
||||
uint64_t swap_total; // Total swap size (byte 단위)
|
||||
uint64_t swap_used; // Used swap size (byte 단위)
|
||||
int swap_usage; // swap 사용률 (%)
|
||||
|
||||
uint64_t storage_total; // Total Storage size (byte 단위)
|
||||
uint64_t storage_avail; // Available Storage size (byte 단위)
|
||||
int storage_max_usage; // Storage 중 최대 사용률 (%)
|
||||
|
||||
uint64_t disk_max_queue; // Disk 상태 정보 중 Max Queue Length
|
||||
double disk_max_busy; // Disk 상태 정보 중 Max busy(%)
|
||||
|
||||
uint64_t network_packet_in; // Network inbound packet 평균 count (count/sec)
|
||||
uint64_t network_packet_out; // Network outbound packet 평균 count (count/sec)
|
||||
uint64_t network_traffic_in; // Network inbound 평균 traffic (Byte/sec)
|
||||
uint64_t network_traffic_out; // Network outbound 평균 traffic (Byte/sec)
|
||||
|
||||
unsigned int tcp_total; // TCP 세션 total count
|
||||
unsigned int tcp_80; // TCP 세션 중 local 80 port 를 사용하는 세션 count
|
||||
unsigned int tcp_80_establish; // TCP 세션 중 local 80 port 를 사용하고.. ESTABLISH 상태인 세션 count
|
||||
};
|
||||
|
||||
// Shared momory 상의 각 slot 구성 정보.
|
||||
struct ClientInfo {
|
||||
|
||||
int use; // 해당 공유메모리 slot 사용 여부 (0 : 미사용, 1 : 사용 중)
|
||||
int valid; // 해당 공유메모리에 저장된 data 의 유효성 여부, (0 : 유효 안함, 1 : 유효)
|
||||
|
||||
char client_ip[IP_ADDR_LENGTH]; // Client IP 주소, NULL 포함 40자까지, IPv6 지원 목적
|
||||
int client_app; // 해당 공유메모리 slot 의 Data 가 어느 프로그램에서 수신되었는지 정의 (0 : csagentd, 1 : vmasd)
|
||||
|
||||
char hostname[HOST_NAME_LENGTH + 1]; // hostname, NULL 포함 최대 64자까지만 허용. 각 장비를 구분하는 Key 값으로 사용.
|
||||
|
||||
struct ServiceInfo service; // 수신된 서비스 상태 정보 저장
|
||||
struct SystemInfo system; // 수신된 System 상태 정보 저장
|
||||
};
|
||||
|
||||
// Shared Memory 전체 구조
|
||||
struct RcInfo {
|
||||
|
||||
int client_max_index; // FHS Client 정보 중 유효한 slot 의 최대 Index 정보.
|
||||
|
||||
struct ClientInfo client[MAX_CLIENT_COUNT]; // 각 FHS Client 정보.
|
||||
|
||||
// 향후 RCDB, RCTS 는 수집 항목이 다르므로.. 아래에 새로 정의하여 확장 처리한다.
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __SHM_DEFINE_H__ */
|
||||
@@ -0,0 +1,45 @@
|
||||
[COMMON]
|
||||
# Log Directory path
|
||||
DEFAULT_LOG_DIR = /user/service/logs
|
||||
|
||||
# Log write level: 7:debug 6:info(Default) 5:notice 4:warning 3:error 2:critical 1:alert 0:emergency
|
||||
#LOG_LEVEL = 6
|
||||
LOG_LEVEL = 7
|
||||
|
||||
# RC ID
|
||||
RC_ID = bd16
|
||||
|
||||
# RCDB Access Information
|
||||
RCDB_IP = 10.3.0.3
|
||||
RCDB_PORT = 6543
|
||||
RCDB_DB_NAME = localdb
|
||||
RCDB_ACCT = syshost
|
||||
RCDB_ACCT_PW = alfkzmf!?{}
|
||||
|
||||
|
||||
#-----------------------------------------------#
|
||||
[rc_balanced]
|
||||
|
||||
# Max Queue Count(size) limit
|
||||
# default: 4096
|
||||
MAX_QUEUE_COUNT = 4096
|
||||
|
||||
# FHS ftsd TCP Listen Port (default: 14001)
|
||||
FHS_TRANSFER_DAEMON_PORT = 14001
|
||||
|
||||
# Rebalance target FHS reselect time limit (minutes)
|
||||
# default 10 (min 5, max 1440)
|
||||
# use transfer total size calc
|
||||
FHS_RESELECTION_TIME_LIMIT = 10
|
||||
|
||||
# Content Hash Check Level
|
||||
# use transfer total size calc. full hash is transfer total size reduce.
|
||||
# 0:Not check (default), 1:Partitial(1M) hash, 2:Full Hash
|
||||
FILE_HASH_CHECK_LEVEL = 0
|
||||
|
||||
# Maximum allowable disk busy of target FHS to move content
|
||||
# default 50, max 80 (%)
|
||||
TARGET_FHS_MAX_DISK_BUSY_LIMIT = 50
|
||||
|
||||
#-----------------------------------------------#
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "Common.h"
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
bool IsRunRcMonD()
|
||||
{
|
||||
char tempBuffer[64];
|
||||
FILE * fd = NULL;
|
||||
bool bRun = false;
|
||||
|
||||
// rc_mond 의 worker 가 동작 중인지 확인을 위해 -n 옵션 사용
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -nx %s | sort", PROCESS_NAME_RC_MONITORING_DAEMON );
|
||||
|
||||
fd = popen( tempBuffer, "r" );
|
||||
if( fd == NULL )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "%s process check fail.[%d][%s]", PROCESS_NAME_RC_MONITORING_DAEMON, errorNum, strerror( errorNum ) );
|
||||
|
||||
// 오류 발생시 false 반환하여.. 해당 프로세스가 동작 중이지 않는 것으로 처리.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
if( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
// 프로세스 ID 가 존재하는 경우...
|
||||
//_LOG( LINF, "[SELECT ] %s process found. pid [%d]", PROCESS_NAME_MONITORING_DAEMON, atoi( tempBuffer ) );
|
||||
bRun = true;
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
return bRun;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/****************************************************************************
|
||||
rc_balanced common header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/26
|
||||
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 __REBALANCE_COMMON_H__
|
||||
#define __REBALANCE_COMMON_H__
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
// rc_mond 동작 여부 check 를 위한 프로세스 name
|
||||
#define PROCESS_NAME_RC_MONITORING_DAEMON "rc_mond"
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// rc_mond 가 기동 상태인지 check 를 위한 함수
|
||||
bool IsRunRcMonD();
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __REBALANCE_COMMON_H__ */
|
||||
|
||||
@@ -0,0 +1,461 @@
|
||||
#include "CopyControlThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "CopyThread.h"
|
||||
#include "ReportLog.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <list>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 5
|
||||
#define SLEEP_TIME_MAX 10
|
||||
|
||||
|
||||
// 본 값은 Makefile 에서 설정
|
||||
//#define MAX_COPY_THREAD_COUNT 2
|
||||
|
||||
|
||||
CCopyControlThread::CCopyControlThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CShmInfoThread * const pShmInfoThread )
|
||||
: m_pQueueMoveStandby( pQueueMoveStandby )
|
||||
, m_pQueueUpdateStandby( pQueueUpdateStandby )
|
||||
, m_pShmInfoThread( pShmInfoThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// Copy Thread count 관련 변수 및 lock 객체 초기화
|
||||
m_nCurrentCopyThreadCount = 0;
|
||||
pthread_mutex_init( &m_mutexCopyThreadCount, NULL );
|
||||
|
||||
m_uTargetFhsSelectFailCount = 0;
|
||||
}
|
||||
|
||||
|
||||
CCopyControlThread::~CCopyControlThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
pthread_mutex_destroy( &m_mutexCopyThreadCount );
|
||||
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueMoveStandby != NULL )
|
||||
m_pQueueMoveStandby = NULL;
|
||||
if( m_pQueueUpdateStandby != NULL )
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
if( m_pShmInfoThread != NULL )
|
||||
m_pShmInfoThread = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CCopyControlThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueMoveStandby == NULL || m_pQueueUpdateStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "CCopyControlThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CCopyControlThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "CCopyControlThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CCopyControlThread::PlusCopyThreadCount()
|
||||
{
|
||||
pthread_mutex_lock( &m_mutexCopyThreadCount );
|
||||
++m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_unlock( &m_mutexCopyThreadCount );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CCopyControlThread::MinusCopyThreadCount()
|
||||
{
|
||||
pthread_mutex_lock( &m_mutexCopyThreadCount );
|
||||
--m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_unlock( &m_mutexCopyThreadCount );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int CCopyControlThread::GetCopyThreadCount()
|
||||
{
|
||||
return m_nCurrentCopyThreadCount;
|
||||
}
|
||||
|
||||
int CCopyControlThread::GetMaxCopyThreadCount()
|
||||
{
|
||||
return MAX_COPY_THREAD_COUNT;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CCopyControlThread::threadFunc( void * arg )
|
||||
{
|
||||
CCopyControlThread * pObject = reinterpret_cast<CCopyControlThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CCopyControlThread::Execute()
|
||||
{
|
||||
// sleep time 조정 처리
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// COPY 처리할 대상 content 없다면...
|
||||
if( m_pQueueMoveStandby->GetSize() == 0 )
|
||||
{
|
||||
// MAX sleep 처리
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[COPY ] standby queue empty. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RCDB update standby queue 에 여유 공간이 있는지 check
|
||||
// - 없는 경우 RCDB 장애 등의 상황이므로... MAX sleep 처리한다.
|
||||
unsigned int uUpdatePushEnableCount = m_pQueueUpdateStandby->GetMaxSize() - m_pQueueUpdateStandby->GetSize();
|
||||
|
||||
// RCDB update standby queue 에 10% 정도 여유 공간이 없으면...
|
||||
if( uUpdatePushEnableCount < (m_pQueueUpdateStandby->GetMaxSize() /10) )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[COPY ] rcdb update queue not enough (%u %u). max sleep %u sec."
|
||||
, uUpdatePushEnableCount, m_pQueueUpdateStandby->GetMaxSize() / 10, m_uSleep );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 생성 가능한 copy thread 가 존재하는지 확인
|
||||
unsigned int uEnableThreadCount = GetCopyThreadEnableCount();
|
||||
|
||||
// 생성 가능한 copy therad 가 없는 경우... 잠시 대기 후 다시 시도
|
||||
if( uEnableThreadCount == 0 )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
_LOG( LDBG, "[COPY ] copy thread all working [%u / %u]. sleep %u sec."
|
||||
, uEnableThreadCount, MAX_COPY_THREAD_COUNT, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// ShmInfoThread 객체에서 COPY 가능 FHS 정보가 유효한지 check
|
||||
// - COPY 가능 FHS 가 없는 경우.... 잠시 대기후 다시 시도
|
||||
if( m_pShmInfoThread->IsValidCopyTargetFhs() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
LOG( LERR, "CopyControlThread: valid copy target FHS not exist. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// RCDB 처리를 수행할 객체 생성
|
||||
// RCDB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
|
||||
DataBase rcdb;
|
||||
|
||||
// RCDB 와 연결을 시도한다.
|
||||
if( rcdb.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, "CopyControlThread: RCDB connection failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// move standby queue 에서 생성 가능한 copy theread 갯수만큼 pop 처리한다.
|
||||
CRebalanceData data;
|
||||
std::list<CRebalanceData> listWork;
|
||||
|
||||
while( uEnableThreadCount > listWork.size() && m_pQueueMoveStandby->GetSize() > 0 )
|
||||
{
|
||||
// Move 대기 queue 에서 data 가져와서 pop 처리하고
|
||||
data = m_pQueueMoveStandby->Front();
|
||||
m_pQueueMoveStandby->Pop();
|
||||
|
||||
// 작업 list 에 등록
|
||||
listWork.push_back( data );
|
||||
}
|
||||
|
||||
_LOG( LINF, "[COPY ] start." );
|
||||
|
||||
// list 에 등록된 각 content 에 대해 해당 content 를 소유한 FHS 정보를 조회한 후 이를 처리할 copy thread 를 생성 처리한다.
|
||||
int nResult = 0;
|
||||
std::list<std::string> listIgnoreFHS;
|
||||
|
||||
while( listWork.empty() == false )
|
||||
{
|
||||
data = listWork.front();
|
||||
listWork.pop_front();
|
||||
|
||||
// RCDB 조회를 통하여 FHS 정보 조회
|
||||
listIgnoreFHS.clear();
|
||||
if( GetMetaHostList( data, rcdb, listIgnoreFHS ) == false )
|
||||
{
|
||||
// meta 정보 조회 실패 또는 해당 content 가 삭제된 상황인 경우...
|
||||
// 해당 함수에서 로깅 했으므로... flow 만 처리
|
||||
// 다시 대기 queue 에 넣을 수 없으므로 그냥 버린다.
|
||||
LOG( LERR, "[COPY ] ignore fhs check fail. throw. [%s svc:%s id:%llu size:%llu]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// COPY 처리할 FHS 선정
|
||||
std::string szTargetHost;
|
||||
|
||||
nResult = m_pShmInfoThread->GetCopyTargetFhs( szTargetHost, listIgnoreFHS );
|
||||
if( nResult == 1 ) // COPY 대상 FHS 가 선정된 경우
|
||||
{
|
||||
data.m_szMoveHost = szTargetHost;
|
||||
}
|
||||
else if( nResult == -1 ) // 복사 가능 물리적 FHS 가 없는 경우
|
||||
{
|
||||
// 다시 대기 queue 에 넣을 수 없으므로. 버린다.
|
||||
LOG( LWAR, "[COPY ] proper copy target FHS not exist. throw. [%s svc:%s id:%llu size:%llu]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
// 다음 loop 는 좀 더 지연 후 동작하도록 한다.
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 복사 가능 물리 FHS 는 존재하지만 ignore 에 포함되어 실제로는 가능 FHS 가 없는 경우
|
||||
// - 다시 대기 queue 에 넣을 수 없으므로. 버린다.
|
||||
LOG( LWAR, "[COPY ] available copy target FHS not exist by ignore list. throw. [%s svc:%s id:%llu size:%llu]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - copy 작업 관련 ignore list 에 의한 FHS 선정 실패가 지정된 횟수를 초과되는 경우..
|
||||
// - content select mode (ContentSelectMode) 를 big size 순서-> RANDOM 모드로 변경 처리 코드 추가
|
||||
|
||||
// Copy 대상 FHS 선택관련 ignore list 에 의한 실패 count 증가 처리
|
||||
m_uTargetFhsSelectFailCount++;
|
||||
|
||||
// 만약 실패 값이 지정된 값 이상인 경우 .. content SELECT mode 를 random mode 로 변경 처리한다.
|
||||
if( m_uTargetFhsSelectFailCount > TARGET_FHS_SELECT_MAX_FAIL )
|
||||
{
|
||||
if( CProcessConfig::GetInstance()->GetContentSelectMode() != SELECT_MODE_RANDOM )
|
||||
{
|
||||
CProcessConfig::GetInstance()->SetContentSelectMode( SELECT_MODE_RANDOM );
|
||||
|
||||
LOG( LWAR, "[COPY ] content select mode change to RANDOM by copy faiil count over [%u/%d]"
|
||||
, m_uTargetFhsSelectFailCount, TARGET_FHS_SELECT_MAX_FAIL );
|
||||
}
|
||||
|
||||
// 실패 횟수 count 변수 초기화
|
||||
m_uTargetFhsSelectFailCount = 0;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Copy 처리를 수행할 Thread 생성 처리
|
||||
CCopyThread * pCopyThread = new CCopyThread( data, m_pQueueUpdateStandby, this );
|
||||
|
||||
// Thread 생성 또는 start 처리 실패시
|
||||
// - thread 생성이 실패할 가능성은 거의 없고..
|
||||
// - 아마 CopyThread 객체의 Start 처리시 인자 check 등에서 오류일 가능성이 높으므로..
|
||||
// - 재시도 하지 않고.. 그냥 skip 처리한다.
|
||||
if( pCopyThread == NULL || pCopyThread->Start() == false )
|
||||
{
|
||||
// thread 정리
|
||||
if( pCopyThread != NULL )
|
||||
{
|
||||
delete pCopyThread;
|
||||
pCopyThread = NULL;
|
||||
}
|
||||
|
||||
// 실패 내역 로깅
|
||||
LOG( LWAR, "[COPY ] copy thread create or start fail. fhs[%s] svc[%s] resouce[%llu]."
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str(), data.m_uResourceId );
|
||||
}
|
||||
}
|
||||
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
unsigned int CCopyControlThread::GetCopyThreadEnableCount( )
|
||||
{
|
||||
unsigned int uEnableCount = 0;
|
||||
|
||||
// lock 설정 후 추가 생성 가능한 Copy thread 계산한다.
|
||||
pthread_mutex_lock( &m_mutexCopyThreadCount );
|
||||
uEnableCount = MAX_COPY_THREAD_COUNT - m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_unlock( &m_mutexCopyThreadCount );
|
||||
|
||||
// 값 보정
|
||||
if( uEnableCount < 0 )
|
||||
uEnableCount = 0;
|
||||
|
||||
return uEnableCount;
|
||||
}
|
||||
|
||||
|
||||
bool CCopyControlThread::GetMetaHostList( CRebalanceData & data, DataBase & rcdb, std::list<std::string> & listHost )
|
||||
{
|
||||
// RCDB 연결은 이미 되어 있는 상태
|
||||
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// 해당 content 를 가진 모든 장비를 조회
|
||||
// - 원본, 복제본은 상관 없으며... cache 는 같은 장비에 있어도 되지만.. 혹시나 해서 제외 목록에 포함
|
||||
// - 삭제된 content 역시 rc_gcmd 가 작업 중 삭제시킬 수 있으므로.. 제외.
|
||||
// - uri 정보는 query 에 적합하게 변환해서 query 수행.
|
||||
// - 원본에 정상적으로 동작하는지 check 한 후 존재할 경우.. 장비 목록을 추출.
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT host_name FROM t_meta_%s WHERE uri = "
|
||||
"( SELECT uri FROM t_meta_%s WHERE uri='%s' AND deleted_yn = 'N' AND is_cache = 'N' "
|
||||
" AND resource_type = 0 AND get_content_length != 0); "
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.GetQueryURI().c_str() );
|
||||
|
||||
rcdb.PgDoExec( szQuery );
|
||||
|
||||
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// Query 수행 실패시
|
||||
LOG( LERR, "[COPY ] ignore fhs meta select error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 중복 URI 가 존재하는 경우...
|
||||
// nResult = -4, Error message [ERROR: more than one row returned by a subquery used as an expression] 발생
|
||||
// 해당 상황인 경우.. 운영자가 조치할 수 있도록 report 로깅 처리
|
||||
if( nResult == -4
|
||||
&& strncmp( rcdb.GetErrorMessage().c_str(), "ERROR: more than one row returned", 34) == 0 )
|
||||
{
|
||||
CReportLog reportLog;
|
||||
std::string strErrorMessage;
|
||||
strErrorMessage.clear();
|
||||
|
||||
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
|
||||
{
|
||||
reportLog.GetLastError( strErrorMessage );
|
||||
LOG( LERR, "[COPY ] report log init failed.[%s]", strErrorMessage.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
reportLog.Write( "ERR", "[%s] [duplicate content. please check and delete. host:%s svc:%s id:%llu uri:'%s' ]"
|
||||
, PROG_NAME
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str(), data.m_uResourceId
|
||||
, data.GetQueryURI().c_str() );
|
||||
|
||||
LOG( LWAR, "[COPY ] duplicate content found. [%s svc:%s id:%llu uri:%s][%s]"
|
||||
, data.m_szOriginHost.c_str(), data.m_szServiceSeq.c_str(), data.m_uResourceId
|
||||
, data.GetQueryURI().c_str(), rcdb.GetErrorMessage().c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = rcdb.GetNoTuples();
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// 오류 발생시
|
||||
LOG( LERR, "[COPY ] copy meta ignore fhs select tuple error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 조회 결과가 없는 경우.
|
||||
// Copy 대상 content 가 삭제된 경우....
|
||||
LOG( LERR, "[COPY ] copy meta ignore fhs select not found. [%d][%s]", nResult, szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 조회 결과가 존재.
|
||||
std::string szTemp;
|
||||
listHost.clear();
|
||||
|
||||
// 조회 목록을 list add.
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
szTemp = rcdb.GetValue( i, 0 );
|
||||
listHost.push_back( szTemp );
|
||||
}
|
||||
}
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
|
||||
// 처리 결과 로깅
|
||||
_LOG( LDBG, "[COPY ] copy meta ignore FHS select ok. [%d][%s]", nResult, szQuery );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/****************************************************************************
|
||||
rc_balanced content copy control thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/13
|
||||
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 __COPY_CONTROL_THREAD_H__
|
||||
#define __COPY_CONTROL_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "RcdbInfo.h"
|
||||
#include "ShmInfoThread.h"
|
||||
#include "Database.h"
|
||||
|
||||
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - copy 작업 관련 ignore list 에 의한 FHS 선정 실패가 본 횟수를 초과되는 경우..
|
||||
// - content select mode (ContentSelectMode) 를 big size 순서-> RANDOM 모드로 변경 처리
|
||||
#define TARGET_FHS_SELECT_MAX_FAIL 5
|
||||
|
||||
|
||||
// CCopyControlThread class
|
||||
// - Move 대기 queue 에 들어 있는 content 를 pop 하여...
|
||||
// - Move target FHS 를 선택한 후...
|
||||
// - Target FHS 로 content copy 처리를 수행하고...
|
||||
// - 최종 해당 Content 를 RCDB update 처리를 위한 대기 queue 에 push 처리를 수행
|
||||
|
||||
class CCopyControlThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CCopyControlThread(
|
||||
CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CShmInfoThread * const pShmInfoThread
|
||||
);
|
||||
|
||||
// 소멸자
|
||||
~CCopyControlThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
// COPY 처리 thread 의 현재 count 값을 1 증가시킨다. (Copy Thread 접근 허용을 위해 public 처리)
|
||||
void PlusCopyThreadCount( void );
|
||||
|
||||
// COPY 처리 thread 의 현재 count 값을 1 감소시킨다. (Copy Thread 접근 허용을 위해 public 처리)
|
||||
void MinusCopyThreadCount( void );
|
||||
|
||||
// Copy 처리 thread 의 현재 count 값을 반환한다.. ( no lock)
|
||||
int GetCopyThreadCount( void );
|
||||
|
||||
// Copy 처리 thread 의 MAX 제한값을 반환한다.. ( no lock)
|
||||
int GetMaxCopyThreadCount( void );
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
|
||||
// Move 처리할 대상을 저장하는 queueMoveStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueMoveStandby;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// Sharde memory 정보를 추출하여 관리하는 Thread 객체에 대한 포인터
|
||||
CShmInfoThread * m_pShmInfoThread;
|
||||
|
||||
// RCDB 연결 정보 관리 객체
|
||||
CRcdbInfo m_rcdbInfo;
|
||||
|
||||
// COPY 처리 thread 의 현재 count 값을 저장하기 위한 변수 및 lock 객체
|
||||
int m_nCurrentCopyThreadCount;
|
||||
pthread_mutex_t m_mutexCopyThreadCount;
|
||||
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - copy 작업 관련 ignore list 에 의한 FHS 선정 실패 횟수 count;
|
||||
// - move 대기 queue 가 0 인 경우 count reset 처리;
|
||||
unsigned int m_uTargetFhsSelectFailCount;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
// 생성 가능한 Copy Thread count 를 반환한다.
|
||||
unsigned int GetCopyThreadEnableCount( void );
|
||||
|
||||
// RCDB 로 부터 해당 content를 가진 FHS 정보를 조회하여 반환한다.
|
||||
bool GetMetaHostList( CRebalanceData & data, DataBase & rcdb, std::list<std::string> & listHost );
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __COPY_CONTROL_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
#include "CopyThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "../ftsd/FtsdSocketControl.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "ReportLog.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
CCopyThread::CCopyThread( CRebalanceData & data
|
||||
, CDataQueue<CRebalanceData>* const pQueueUpdateStandby
|
||||
, CCopyControlThread * const pControl )
|
||||
: m_data( data ), m_pQueueUpdateStandby( pQueueUpdateStandby ), m_pControl(pControl)
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
m_threadHandle = 0;
|
||||
|
||||
}
|
||||
|
||||
CCopyThread::~CCopyThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
m_pControl = NULL;
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
||||
bool CCopyThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueUpdateStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "CopyThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
if( m_pControl == NULL )
|
||||
{
|
||||
LOG( LERR, "CopyThread: CCopyControlThread object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업할 data 객체의 각 항목이 유효한지 check
|
||||
|
||||
// 1. copy target host check
|
||||
if( m_data.m_szMoveHost.size() < 7 )
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy target host[%s] not valid.", m_data.m_szMoveHost.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. origin host & origin content check
|
||||
if( m_data.m_szOriginHost.size() < 7 || m_data.m_szOriginFileNameHash.size() < 5 )
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy origin host[%s], filename[%s] not valid."
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szOriginFileNameHash.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CCopyThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "CopyThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/////////////////////////////////
|
||||
|
||||
void * CCopyThread::threadFunc( void * arg )
|
||||
{
|
||||
CCopyThread * pObject = reinterpret_cast<CCopyThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
// Thread 동작시 control class 에 count 증가 처리
|
||||
pObject->m_pControl->PlusCopyThreadCount();
|
||||
|
||||
// 실제 작업 수행
|
||||
pObject->Execute();
|
||||
|
||||
// Thread 동작 완료시 control class 에 count 감소 처리
|
||||
pObject->m_pControl->MinusCopyThreadCount();
|
||||
|
||||
pthread_testcancel();
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
// 객체 자동 delete 처리
|
||||
delete pObject;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CCopyThread::Execute( )
|
||||
{
|
||||
// FHS ftsd 와 통신 처리 담당한 socket 객체 생성
|
||||
CFtsdSocketControl fhs;
|
||||
|
||||
// origin FHS connect
|
||||
bool bResult
|
||||
= fhs.ConnectTarget( m_data.m_szOriginHost, CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() );
|
||||
|
||||
if( bResult == false )
|
||||
{
|
||||
// origin fhs connect fail.
|
||||
LOG( LERR, "CopyThread: origin fhs[%s][%u] connect fail. [%s]"
|
||||
, m_data.m_szOriginHost.c_str(), CProcessConfig::GetInstance()->GetFhsTransferDaemonPort()
|
||||
, m_data.m_szURI.c_str() );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// target FHS 정보를 vector 로 저장 처리
|
||||
std::vector<std::string> vecTargetFHS;
|
||||
vecTargetFHS.clear();
|
||||
vecTargetFHS.push_back( m_data.m_szMoveHost );
|
||||
|
||||
|
||||
// 연결된 FHS 로 content copy 처리 요청 전달.
|
||||
bResult = fhs.SendFileReplicationRequest( m_data.m_szOriginFileNameHash
|
||||
, m_data.m_uOriginContentLength
|
||||
, CProcessConfig::GetInstance()->GetHashCheckLevel()
|
||||
, true
|
||||
, vecTargetFHS );
|
||||
|
||||
if( bResult == false )
|
||||
{
|
||||
// content copy 요청 실패
|
||||
LOG( LERR, "CopyThread: content copy request send fail to origin fhs.[%s][%s]"
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szURI.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;
|
||||
std::map<std::string, std::string> mapSuccessFHS;
|
||||
std::map<std::string, std::string> mapFailFHS;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
nResult = fhs.GetFileReplicationResult( 3, bSuccess, szErrorMessage, mapSuccessFHS, mapFailFHS );
|
||||
|
||||
if( nResult == 0 )
|
||||
{
|
||||
// 지정된 시간동안 결과 수신 못한 경우
|
||||
if( fhs.SendAliveCheck() == false )
|
||||
{
|
||||
// 다음 loop 에서 -1 연결 종료 처리되므로.. 여기서는 로깅만..
|
||||
LOG( LERR, "CopyThread: content copy alive check fail.[%s][%s]"
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szURI.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 )
|
||||
{
|
||||
// Copy 처리 결과가.. mapSuccessFHS 에 저장된 상태면.. 성공
|
||||
// mapFailFHS 에 저장된 상태면 뭔가 오류가 발생한 상황으로 판단해야 함
|
||||
std::map<std::string, std::string>::iterator itFHS;
|
||||
|
||||
if( mapSuccessFHS.empty() == false )
|
||||
{
|
||||
// 성공 FHS 목록에 존재하는 경우... 정상 copy 처리된 것으로 판단
|
||||
itFHS = mapSuccessFHS.begin();
|
||||
|
||||
m_data.m_uMoveResult = MOVE_OK;
|
||||
m_data.m_szMoveHost = itFHS->first;
|
||||
m_data.m_szMoveFileNameHash = itFHS->second;
|
||||
m_data.m_uMoveContentLength = m_data.m_uOriginContentLength;
|
||||
|
||||
// RCDB update queue 에 PUSH 처리
|
||||
if( m_pQueueUpdateStandby->Push( m_data ) == false )
|
||||
{
|
||||
// 실패시 3 sec sleep 후 1회만 재시도.
|
||||
sleep( 3 );
|
||||
if( m_pQueueUpdateStandby->Push( m_data ) == false )
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy ok [%.2lf sec]. but update queue push fail. OP manual delete need.[%s %s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szMoveHost.c_str(), m_data.m_szMoveFileNameHash.c_str() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 관련 내역 로깅
|
||||
_LOG( LINF, "CopyThread[%lu]: copy ok. origin [%s svc:%s id:%llu size:%llu uri:%s]"
|
||||
, pthread_self()
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szURI.c_str() );
|
||||
_LOG( LINF, "CopyThread[%lu]: copy ok [%.2lf sec]. target [%s size:%llu %s]"
|
||||
, pthread_self()
|
||||
, timeElapsed
|
||||
, m_data.m_szMoveHost.c_str(), m_data.m_uMoveContentLength, m_data.m_szMoveFileNameHash.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// fail 목록에 포함된 상태.
|
||||
if( mapFailFHS.empty() == false )
|
||||
{
|
||||
itFHS = mapFailFHS.begin();
|
||||
|
||||
m_data.m_uMoveResult = MOVE_ERROR_CREATE;
|
||||
m_data.m_szMoveHost = itFHS->first;
|
||||
szErrorMessage = itFHS->second;
|
||||
|
||||
// 실패했으므로.. RCDB update queue 에 push 하지 않고...
|
||||
// 로깅만 하고 종료한다.
|
||||
LOG( LERR, "CopyThread: copy fail [%.2lf sec]. [%s svc:%s id:%llu size:%llu]->[%s][%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str(), szErrorMessage.c_str()
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// success, fail map 이 모두 빈 상태.. ?
|
||||
// 이런 상황은 발생하면 안되는데...
|
||||
LOG( LERR, "CopyThread: copy fail [%.2lf sec]. success-fail map all empty.[%s svc:%s id:%llu size:%llu]->[%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// content 이상으로 copy 처리하지 못한 경우.
|
||||
LOG( LERR, "CopyThread: copy fail [%.2lf sec]. check content [%s svc:%s id:%llu size:%llu]->[%s][%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str(), szErrorMessage.c_str()
|
||||
);
|
||||
|
||||
// source file access 오류인 경우..
|
||||
// - report 로깅
|
||||
if( strncmp( szErrorMessage.c_str(), "source file access fail", 23 ) == 0 )
|
||||
{
|
||||
CReportLog reportLog;
|
||||
std::string errorMessage;
|
||||
errorMessage.clear();
|
||||
|
||||
if( reportLog.Init( CProcessConfig::GetInstance()->GetLogPath() ) == false )
|
||||
{
|
||||
reportLog.GetLastError( errorMessage );
|
||||
LOG( LERR, "CopyThread: report log init failed.[%s]", errorMessage.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
reportLog.Write( "ERR", "[%s] [content access fail. please check. host:%s svc:%s id:%llu uri:'%s' ]"
|
||||
, PROG_NAME
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str(), m_data.m_uResourceId
|
||||
, m_data.GetQueryURI().c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 처리 결과 수신에 따른 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, "CopyThread: copy fail [%.2lf sec]. fhs connection broken.[%s svc:%s id:%llu size:%llu]->[%s]"
|
||||
, timeElapsed, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str() );
|
||||
|
||||
break;
|
||||
}
|
||||
else if( nResult == 2 )
|
||||
{
|
||||
// Replication 응답이 아닌 경우.. 무시
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 미정의된 상황
|
||||
LOG( LERR, "CopyThread: receive undefine result.[%d] [%s][%s][%llu]->[%s]"
|
||||
, nResult
|
||||
, m_data.m_szURI.c_str(), m_data.m_szOriginHost.c_str(), m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.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 > 1200.0 ) // 20분 이상 대기하는 경우 - 장비별 재선택 제한시간이 20 분 정도이므로..
|
||||
{
|
||||
LOG( LERR, "CopyThread: copy fail. response wait %.2lf sec, but timeout 1200 sec.[%s svc:%s id:%llu size:%llu]->[%s]"
|
||||
, timeElapsed
|
||||
, m_data.m_szOriginHost.c_str(), m_data.m_szServiceSeq.c_str()
|
||||
, m_data.m_uResourceId, m_data.m_uOriginContentLength
|
||||
, m_data.m_szMoveHost.c_str() );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
} // while(1)
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/****************************************************************************
|
||||
rc_balanced copy thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/22
|
||||
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 __CONTENT_COPY_THREAD_H__
|
||||
#define __CONTENT_COPY_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "CopyControlThread.h"
|
||||
|
||||
|
||||
// CCopyThread class
|
||||
// - 전달받은 content 정보를 이용하여...
|
||||
// - 실제 FHS 의 ftsd 와 통신을 하여 Content COPY 처리를 수행
|
||||
class CCopyThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CCopyThread(
|
||||
CRebalanceData & data
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CCopyControlThread * const pControl );
|
||||
|
||||
// 소멸자
|
||||
~CCopyThread();
|
||||
|
||||
// Thread 생성 및 Copy 작업 시작
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// 작업 대상 content 정보 저장을 위한 객체
|
||||
CRebalanceData m_data;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// thread 상태를 check 하는 CopyControlThread 객체에 대한 포인터
|
||||
CCopyControlThread * m_pControl;
|
||||
|
||||
private:
|
||||
|
||||
// Thread function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 content copy 처리를 수행
|
||||
void Execute( void );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __CONTENT_COPY_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/****************************************************************************
|
||||
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 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 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__ */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__
|
||||
@@ -0,0 +1,464 @@
|
||||
#include "Main.h"
|
||||
|
||||
#include "ProcessRename.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include "WorkerRebalance.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>
|
||||
|
||||
|
||||
// 실제 content 분산 처리를 담당하는 Rebalance Worker PID 정보.
|
||||
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
|
||||
pid_t g_pidWorkerRebalance = -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, "RC ID : %s", CProcessConfig::GetInstance()->GetRcId() );
|
||||
_LOG( LINF, "RCDB : %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 copy thread count : %d", MAX_COPY_THREAD_COUNT );
|
||||
_LOG( LINF, "FHS reselection time limit : %u (min)", CProcessConfig::GetInstance()->GetFhsReselectionTimeLimit() );
|
||||
_LOG( LINF, "Move content hash check : %u (0:not check, 1:partitial, 2:full)", CProcessConfig::GetInstance()->GetHashCheckLevel() );
|
||||
_LOG( LINF, "Target FHS disk busy limit : %.1lf %%", CProcessConfig::GetInstance()->GetTargetFhsMaxDiskBusyLimit() );
|
||||
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
|
||||
// Log Level 재설정. -> conf 설정대로 변경 처리.
|
||||
#ifdef _DEBUG_
|
||||
CLogger::GetInstance()->SetLogLevel( LDBG );
|
||||
#else
|
||||
CLogger::GetInstance()->SetLogLevel( CProcessConfig::GetInstance()->GetLogLevel() );
|
||||
#endif
|
||||
|
||||
|
||||
// Worker Process 생성
|
||||
// 실제 Content 분산 처리를 수행할 Worker 프로세스 생성.
|
||||
if( MakeWorkerRebalance() == false )
|
||||
{
|
||||
LOG( LERR, "[Main] Rebalance worker process create failed. => Process exit." );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// 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 ReadyToExit()
|
||||
{
|
||||
|
||||
// Logger 객체 종료 처리.
|
||||
CLogger::Exit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 사용방법 표시
|
||||
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, " - automatically content rebalancing in FHS saved content.\n" );
|
||||
fprintf( stderr, " - rc_mond must be running first.\n\n" );
|
||||
|
||||
fprintf( stderr, "process title explain.\n" );
|
||||
fprintf( stderr, " - S: move standby content count.\n" );
|
||||
fprintf( stderr, " - T: content transfer job count.\n" );
|
||||
fprintf( stderr, " - U: copy complete content count. DB update standby.\n" );
|
||||
fprintf( stderr, " - D: unlink standby content count.\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 );
|
||||
}
|
||||
|
||||
// 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_pidWorkerRebalance )
|
||||
{
|
||||
_LOG( LWAR, "[Main] Rebalance worker process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Unknown child process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// signal 에 종료된 상황이 아닌 경우...
|
||||
// - 각 Worker 의 초기화 실패 발생시... return 구문에 의해 종료됨.
|
||||
// - 기타 등등.
|
||||
|
||||
// 관련 내역 로깅.
|
||||
if( deadPid == g_pidWorkerRebalance )
|
||||
{
|
||||
_LOG( LWAR, "[Main] Rebalance worker process[%d] killed. Not signal.", deadPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Unknown child process[%d] killed. Not signal", deadPid );
|
||||
}
|
||||
|
||||
// 만약 Worker 프로세스들이 초기화 실패로 인해 EXIT_FAILURE 반환되는 상황이라면...
|
||||
// 자식 프로세스를 재생성하지 않고.. 프로세스를 종료 처리한다.
|
||||
if( WIFEXITED( nDeadStatus ) )
|
||||
{
|
||||
if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
|
||||
{
|
||||
if( deadPid == g_pidWorkerRebalance )
|
||||
{
|
||||
_LOG( LWAR, "[Main] Rebalace worker process[%d] initialize failed.", deadPid );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Unknown child process[%d] exit with fail.", deadPid );
|
||||
}
|
||||
|
||||
_LOG( LERR, "[Main] Program abnormally exited because Worker initialize faild.." );
|
||||
|
||||
// Main 종료 시그널 발생
|
||||
raise( SIGTERM );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 위에서 로깅 및 초기화 실패로 인한 Main 종료 처리 작업을 수행했으므로...
|
||||
// 죽은 Worker 프로세스를 재생성 처리한다.
|
||||
if( deadPid == g_pidWorkerRebalance )
|
||||
{
|
||||
if( MakeWorkerRebalance() == false )
|
||||
{
|
||||
_LOG( LERR, "[Main] Rebalance worker process recreate failed.=> Main process exit." );
|
||||
raise( SIGTERM );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Main] Worker process recreate success." );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 오류 발생시 해당 내역 로깅
|
||||
if( deadPid < 0 )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[Main] SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Main 프로세스에 대한 종료 처리 수신시
|
||||
static void SignalMainTerminate( int nSignalNumber )
|
||||
{
|
||||
// Rebalance worker 프로세스를 종료 처리한다.
|
||||
if( g_pidWorkerRebalance != -1 )
|
||||
{
|
||||
kill( g_pidWorkerRebalance, 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 분산을 담당할 Worker 프로세스 생성.
|
||||
bool MakeWorkerRebalance()
|
||||
{
|
||||
// fork() 결과를 Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장
|
||||
g_pidWorkerRebalance = fork();
|
||||
|
||||
if( g_pidWorkerRebalance == 0 )
|
||||
{
|
||||
// 생성된 Worker 프로세스인 경우...
|
||||
int iResult;
|
||||
|
||||
// Rebalance Worker 프로세스의 Main 함수 호출
|
||||
iResult = WorkerRebalanceMain();
|
||||
|
||||
// Worker 프로세스의 Main 함수 종료시 종료 처리
|
||||
_LOG( LINF, "[Main] Rebalance worker process exit. PID[%d] return[%d]", getpid(), iResult );
|
||||
|
||||
ReadyToExit();
|
||||
exit( iResult );
|
||||
}
|
||||
else if( g_pidWorkerRebalance < 0 )
|
||||
{
|
||||
// fork 실패시
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[Main] Rebalance worker process create failed. [%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 현재의 Main 프로세스
|
||||
_LOG( LINF, "[Main] Rebalance worker process created.. PID[%d]", g_pidWorkerRebalance );
|
||||
|
||||
// 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 200000000; // 0.2 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/****************************************************************************
|
||||
Main.h for rc_balanced
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/02/22
|
||||
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 __RC_BALANCED_MAIN_H__
|
||||
#define __RC_BALANCED_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 분산 처리를 담당할 Worker 프로세스 생성.
|
||||
/// @return 생성 성공시 true, 실패시 false
|
||||
bool MakeWorkerRebalance();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __RC_BALANCED_MAIN_H__ */
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#****************************************************************************
|
||||
# Makefile for rc_balanced
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2019/02/22
|
||||
# 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_balanced
|
||||
REVISION = 1531
|
||||
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
|
||||
|
||||
DEFAULT_CONFIG_FILE = /user/service/etc/rcts.conf
|
||||
|
||||
# COPY 처리를 수행하는 Copy Thread 의 Max count 값 설정
|
||||
# - 3 이상 값으로 할 경우.. MOVE 수신 FHS 가 1대인 경우.. Write 처리 관련 부하 발생
|
||||
# - recommand: 1~2 , max: 10
|
||||
MAX_COPY_THREAD_COUNT = 2
|
||||
|
||||
|
||||
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)\" -DMAX_COPY_THREAD_COUNT=$(MAX_COPY_THREAD_COUNT)
|
||||
|
||||
# Application Enviroment
|
||||
APP = $(PROG_NAME)
|
||||
|
||||
DIR_INCLUDE = -I../lib -I../ftsd -I../rc_mond -I/user/db/pgsql/include
|
||||
DIR_LIB = -L../lib
|
||||
|
||||
|
||||
OBJ = ProcessConfig.o RebalanceData.o Common.o \
|
||||
../ftsd/FtsdSocketControl.o RcdbInfo.o Database.o CopyThread.o ShmInfoThread.o \
|
||||
ProcessMonitoringThread.o TargetSelectThread.o CopyControlThread.o RcdbUpdateThread.o PhysicalDeleteThread.o \
|
||||
WorkerRebalance.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 ../rc_mond/*.o ../rc_mond/*.out ../rc_mond/core.*
|
||||
-rm -f $(APP)
|
||||
sync
|
||||
|
||||
|
||||
install : $(APP)
|
||||
-cp $(PROG_NAME) $(INSTALL_BIN)/$(PROG_NAME)
|
||||
sync
|
||||
|
||||
|
||||
# End of Makefile
|
||||
|
||||
@@ -0,0 +1,381 @@
|
||||
#include "PhysicalDeleteThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "../ftsd/FtsdSocketControl.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 10
|
||||
#define SLEEP_TIME_MAX 20
|
||||
|
||||
// unlink 처리 실패시 retry queue 에 얼마동안 대기할 것인지 시간 (sec 단위)
|
||||
#define RETRY_STANDBY_MAX_TIME 1800
|
||||
|
||||
// unlink 처리 실패시 retry 가능 횟수, 최초 실패 후 2회까지 RETRY 한다는 의미
|
||||
#define FAIL_MAX_COUNT 3
|
||||
|
||||
|
||||
CPhysicalDeleteThread::CPhysicalDeleteThread( CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CProcessMonitoringThread * pMonitoringThread )
|
||||
: m_pQueueDeleteStandby( pQueueDeleteStandby ), m_pMonitoringThread( pMonitoringThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// 내부 retry queue 의 최대 크기 설정 (장기 보관 땜시 기본 설정의 2배)
|
||||
m_queueDeleteRetry.SetMaxSize( 2 * (CProcessConfig::GetInstance()->GetMaxQueueSize()) );
|
||||
}
|
||||
|
||||
CPhysicalDeleteThread::~CPhysicalDeleteThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueDeleteStandby != NULL )
|
||||
m_pQueueDeleteStandby = NULL;
|
||||
if( m_pMonitoringThread != NULL )
|
||||
m_pMonitoringThread = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CPhysicalDeleteThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueDeleteStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "PhysicalDeleteThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
if( m_pMonitoringThread == NULL )
|
||||
{
|
||||
LOG( LERR, "PhysicalDeleteThread: CProcessMonitoringThread object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CPhysicalDeleteThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "PhysicalDeleteThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CPhysicalDeleteThread::threadFunc( void * arg )
|
||||
{
|
||||
CPhysicalDeleteThread * pObject = reinterpret_cast<CPhysicalDeleteThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CPhysicalDeleteThread::Execute()
|
||||
{
|
||||
// retry queue 및 delete queue 가 비어 있다먼.. MAX sleep 한다.
|
||||
if( m_queueDeleteRetry.GetSize() == 0 && m_pQueueDeleteStandby->GetSize() == 0 )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[DELETE ] standby, retry queue empty. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG( LINF, "[DELETE ] start." );
|
||||
|
||||
CRebalanceData data;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
// 삭제 처리할 content 를 queue 에서 pop 하여 가져온다.
|
||||
// pop 할 content 가 없으면.. sleep time 조정 후 loop 종료
|
||||
if( Pop( data ) == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[DELETE ] proper content not found. max sleep %u sec.", m_uSleep );
|
||||
break;
|
||||
}
|
||||
|
||||
// ftsd 와 통신을 통해 unlink 처리를 수행한다.
|
||||
|
||||
if( Delete( data ) == true )
|
||||
{
|
||||
// unlink 성공
|
||||
// -> Origin content 를 삭제한 경우 Process 모니터링 모듈에 삭제한 FHS, size 정보 전송
|
||||
// -> move 처리 실패로 삭제한 내역은 Process 모니터링 모듈에 전송하지 않는다.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
// Process monitoring 모듈에 삭제한 FHS, size 관련 정보 전달 기능 추가
|
||||
m_pMonitoringThread->UpdateDeletedInfo( data );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// unlink 실패
|
||||
// -> Retry queue 에 다시 넣던지.. 아니면 버리던지 한다.
|
||||
data.m_uDeleteFailCount +=1;
|
||||
if( data.m_uDeleteFailCount > FAIL_MAX_COUNT )
|
||||
{
|
||||
// 재시도 횟수 초과인 경우... 로깅 후 버린다.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry count over.[origin %s %s %llu]"
|
||||
, data.m_szOriginHost.c_str()
|
||||
, data.m_szOriginFileNameHash.c_str()
|
||||
, data.m_uOriginContentLength );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry count over.[target %s %s %llu]"
|
||||
, data.m_szMoveHost.c_str()
|
||||
, data.m_szMoveFileNameHash.c_str()
|
||||
, data.m_uMoveContentLength );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// retry queue 에 대한 push 처리한다.
|
||||
data.m_timeLastDeleteFail = time( 0 );
|
||||
|
||||
if( m_queueDeleteRetry.Push( data ) == false )
|
||||
{
|
||||
// push 실패 로깅 후 버린다.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry queue push fail.[origin %s size:%llu %s]"
|
||||
, data.m_szOriginHost.c_str()
|
||||
, data.m_uOriginContentLength
|
||||
, data.m_szOriginFileNameHash.c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink retry queue push fail.[target %s size:%llu %s]"
|
||||
, data.m_szMoveHost.c_str()
|
||||
, data.m_uMoveContentLength
|
||||
, data.m_szMoveFileNameHash.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // unlink 실패
|
||||
|
||||
} // while(1)
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool CPhysicalDeleteThread::Pop( CRebalanceData & data )
|
||||
{
|
||||
time_t timeNow;
|
||||
|
||||
// 현재 시간 정보 추출
|
||||
time( &timeNow );
|
||||
|
||||
// 우선 retry queue 에서 대상이 존재하는지 확인하여 pop 처리
|
||||
while( m_queueDeleteRetry.GetSize() > 0 )
|
||||
{
|
||||
data = m_queueDeleteRetry.Front();
|
||||
|
||||
// 재시도 횟수값 확인
|
||||
// - 실제로는 queue 에 push 처리할 때 확인해서 처리하지만....
|
||||
// - 혹시 오류가 발생할 수 있어 방어 목적으로 확인한다.
|
||||
if( data.m_uDeleteFailCount > FAIL_MAX_COUNT )
|
||||
{
|
||||
// 재시도 횟수가 over 된 경우 버리고.. 다시 pop
|
||||
m_queueDeleteRetry.Pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
// delete 처리 관련 마지막 실패 시각을 확인하여...
|
||||
// 지정된 대기 시간이 안 된 경우...
|
||||
if( (timeNow - data.m_timeLastDeleteFail) < RETRY_STANDBY_MAX_TIME )
|
||||
{
|
||||
// retry queue 검색을 중단한다.
|
||||
break;
|
||||
}
|
||||
|
||||
// retry queue 에서 해당 content 를 POP 하고...
|
||||
// delete 작업을 수행하도록 true 를 반환한다.
|
||||
m_queueDeleteRetry.Pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 삭제 대기 queue 에서 pop 처리 수행
|
||||
while( m_pQueueDeleteStandby->GetSize() > 0 )
|
||||
{
|
||||
data = m_pQueueDeleteStandby->Front();
|
||||
m_pQueueDeleteStandby->Pop();
|
||||
|
||||
// 해당 객체의 처리 결과를 확인하여 삭제 작업이 필요없는 content 인 경우....
|
||||
// 버리고 다시 pop 한다.
|
||||
if( data.m_uMoveResult == MOVE_ERROR_CREATE )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// queue 에서 적합한 content 를 pop 하지 못한 경우... false 반환.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CPhysicalDeleteThread::Delete( CRebalanceData & data )
|
||||
{
|
||||
// ftsd 와 통신할 socket 객체 생성
|
||||
CFtsdSocketControl ftsd;
|
||||
|
||||
// 작업 대상 content 정보를 저장할 변수
|
||||
std::string szHost;
|
||||
std::string szFileNameHash;
|
||||
unsigned long long uContentLength;
|
||||
unsigned long long uResourceId;
|
||||
|
||||
// 전달받은 content 의 처리 결과를 확인하여..어떤 물리 파일을 삭제할 것인지 판단하여 처리
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
// origin 의 content 를 삭제하도록 한다.
|
||||
szHost = data.m_szOriginHost;
|
||||
szFileNameHash = data.m_szOriginFileNameHash;
|
||||
uContentLength = data.m_uOriginContentLength;
|
||||
uResourceId = data.m_uResourceId;
|
||||
}
|
||||
else
|
||||
{
|
||||
// MOVE 처리 실패로.. target fhs 에 잘못 생성된 content 를 삭제해야 하는 경우..
|
||||
szHost = data.m_szMoveHost;
|
||||
szFileNameHash = data.m_szMoveFileNameHash;
|
||||
uContentLength = data.m_uMoveContentLength;
|
||||
uResourceId = 0;
|
||||
}
|
||||
|
||||
// FHS 의 ftsd 에 접속
|
||||
if( ftsd.ConnectTarget( szHost, (CProcessConfig::GetInstance()->GetFhsTransferDaemonPort()) ) == false )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] fhs: %s (%s) connect fail."
|
||||
, szHost.c_str(), (data.m_uMoveResult == MOVE_OK ? "origin" : "target") );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 접속한 ftsd 로 unlink 처리 요청 전달.
|
||||
if( ftsd.SendFileUnlinkRequest( szFileNameHash, uContentLength, true ) == false )
|
||||
{
|
||||
_LOG( LERR, "[DELETE ] unlink request send fail. [%s %s id:%llu size:%llu %s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str() );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 정보 로깅
|
||||
_LOG( LINF, "[DELETE ] unlink request send ok. [%s %s id:%llu size:%llu %s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str() );
|
||||
|
||||
|
||||
int nTimeout = 5; // 응답 대기 시간 (5 sec)
|
||||
int nResult = 0;
|
||||
bool bResultSuccess; // 처리 결과의 성공, 실패 여부를 저장하기 위한 변수
|
||||
std::string szErrorMessage;
|
||||
|
||||
// ftsd 로 부터 응답 수신 대기
|
||||
while( 1 )
|
||||
{
|
||||
// nTimeout 에 지정된 시간동안 응답 대기 시도
|
||||
nResult = ftsd.GetFileUnlinkResult( nTimeout, bResultSuccess, szErrorMessage );
|
||||
|
||||
if( nResult == 1 )
|
||||
{
|
||||
// 요청에 대한 처리 결과 수신
|
||||
if( bResultSuccess == true )
|
||||
{
|
||||
// 요청에 대한 처리가 정상적으로 처리된 경우
|
||||
_LOG( LINF, "[DELETE ] unlink ok. [%s %s id:%llu size:%llu %s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str() );
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 오류 발생시
|
||||
_LOG( LERR, "[DELETE ] unlink error.[%s %s id:%llu size:%llu %s] [%s]"
|
||||
, (data.m_uMoveResult == MOVE_OK ? "origin" : "target")
|
||||
, szHost.c_str(), uResourceId, uContentLength, szFileNameHash.c_str(), szErrorMessage.c_str() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 응답 대기 루프 종료
|
||||
break;
|
||||
}
|
||||
else if( nResult == -1 )
|
||||
{
|
||||
// socket 오류, 접속 종료 등이 발생한 경우
|
||||
_LOG( LERR, "[DELETE ] ftsd reponse wait fail.[%s %s]"
|
||||
, szHost.c_str(), szFileNameHash.c_str() );
|
||||
break;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우
|
||||
// -> Allive Check 패킷 한번 쏘고 다시 Loop 로
|
||||
if( ftsd.SendAliveCheck() == false )
|
||||
{
|
||||
// alive 전송 실패
|
||||
// -> socket 종료 또는 오류 발생한 경우
|
||||
_LOG( LERR, "[DELETE ] ftsd reponse wait fail2.[%s %s]"
|
||||
, szHost.c_str(), szFileNameHash.c_str() );
|
||||
break;
|
||||
}
|
||||
// 응답대기
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unlink 요청에 대한 응답 패킷이 아닌 경우
|
||||
// -> 무시하고 다시 대기
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/****************************************************************************
|
||||
rc_balanced content physical delete thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/13
|
||||
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 __PHYSICAL_DELETE_THREAD_H__
|
||||
#define __PHYSICAL_DELETE_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "ProcessMonitoringThread.h"
|
||||
|
||||
|
||||
// CPhysicalDeleteThread class
|
||||
// - delete standby queue 에 들어 있는 content 를 pop 하여...
|
||||
// - 각 FHS 의 ftsd 로 unlink 처리 요청을 전송한다.
|
||||
// - 만약 unlink 처리 실패 발생한 경우.. retry queue 에 저장한 후 지정된 시간 이후 재시도 처리한다.
|
||||
class CPhysicalDeleteThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CPhysicalDeleteThread( CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CProcessMonitoringThread * pMonitoringThread );
|
||||
|
||||
// 소멸자
|
||||
~CPhysicalDeleteThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
|
||||
// unlink 처리 대상 content 정보를 저장하는 queueDeleteStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueDeleteStandby;
|
||||
|
||||
// unlink 처리 실패시 retry 처리를 수행하기 위한 내부 queue 객체 (외부에서 전달받은 객체가 아닌 내부 멤버객체임)
|
||||
CDataQueue<CRebalanceData> m_queueDeleteRetry;
|
||||
|
||||
|
||||
// CProcessMonitoringThread 객체에 대한 포인터
|
||||
// - delete 작업 통계 정보 업데이트를 위한 객체 포인터, 생성자에서 전달받음
|
||||
CProcessMonitoringThread * m_pMonitoringThread;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
|
||||
// Delete queue , Retry queue 에서 삭제 작업을 수행할 content 를 POP 처리하여
|
||||
// 전달받은 참조 인자로 전달한다.
|
||||
// pop 처리할 content 가 없는 경우 false 반환.
|
||||
bool Pop( CRebalanceData &data );
|
||||
|
||||
// 실제 ftsd 와 통신을 통해 인자로 전달받은 content unlink 처리 수행
|
||||
bool Delete( CRebalanceData &data );
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PHYSICAL_DELETE_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 default 값 추가
|
||||
#define DEFAULT_TARGET_FHS_MAX_DISK_BUSY_LIMIT 50.0
|
||||
|
||||
|
||||
// static 변수 초기화
|
||||
CProcessConfig * CProcessConfig::m_pInstance = NULL;
|
||||
|
||||
|
||||
// 생성자
|
||||
CProcessConfig::CProcessConfig()
|
||||
{
|
||||
// default 값 설정.
|
||||
|
||||
// Max Queue Size ( default: 4096 )
|
||||
m_nMaxQueueSize = 4096;
|
||||
|
||||
// FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
|
||||
m_nFhsTransferDaemonPort = 14001;
|
||||
|
||||
// Rebalance target FHS reselect time limit( min ), default 10 min, use transfer total size calc.
|
||||
m_nFhsReselectionTimeLimit = 10;
|
||||
|
||||
// Content 복제 및 Cache 관련 전송시 Hash Check Level
|
||||
// 0:Not check (default), 1:Partitial hash, 2:Full Hash
|
||||
m_nHashCheckLevel = 0;
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - default 는 큰 파일 순서대로 이동 처리 시도...
|
||||
m_nContentSelectMode = SELECT_MODE_SIZE;
|
||||
memset( &m_timeContentSelectModeChange, 0x00, sizeof( struct timespec ) );
|
||||
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 default 값 추가
|
||||
m_dTargetFhsMaxDiskBusyLimit = DEFAULT_TARGET_FHS_MAX_DISK_BUSY_LIMIT;
|
||||
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
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();
|
||||
}
|
||||
|
||||
// RCID
|
||||
if( GetConfigValue( conf, strProgramName, "RC_ID", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strRcId = strValue;
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
// MAX_QUEUE_COUNT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue(conf, strProgramName, "MAX_QUEUE_COUNT", strValue, strErrorMessage) == true)
|
||||
{
|
||||
m_nMaxQueueSize = atoi(strValue.c_str());
|
||||
|
||||
// Queue 역시 1~ 65535 까지만 설정할 수 있도록 한다.
|
||||
if( m_nMaxQueueSize <= 0 || m_nMaxQueueSize > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [MAX_QUEUE_COUNT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// FHS_RESELECTION_TIME_LIMIT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue( conf, strProgramName, "FHS_RESELECTION_TIME_LIMIT", strValue, strErrorMessage ) == true )
|
||||
{
|
||||
m_nFhsReselectionTimeLimit = atoi( strValue.c_str() );
|
||||
|
||||
if( m_nFhsReselectionTimeLimit < 5 || m_nFhsReselectionTimeLimit > 1440 )
|
||||
{
|
||||
strErrorMessage = "Config [FHS_RESELECTION_TIME_LIMIT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// FILE_HASH_CHECK_LEVEL
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
if( GetConfigValue(conf, strProgramName, "FILE_HASH_CHECK_LEVEL", strValue, strErrorMessage) == true )
|
||||
{
|
||||
m_nHashCheckLevel = atoi(strValue.c_str());
|
||||
|
||||
if( m_nHashCheckLevel < 0 || m_nHashCheckLevel > 2 )
|
||||
{
|
||||
strErrorMessage = "Config [FILE_HASH_CHECK_LEVEL] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
// TARGET_FHS_MAX_DISK_BUSY_LIMIT
|
||||
// - 설정이 없는 경우 default 값 사용
|
||||
// - conf 설정은 정수, 저장 변수는 double type 이므로.. 설정값 존재지 형 변환 처리 필요.
|
||||
if( GetConfigValue( conf, strProgramName, "TARGET_FHS_MAX_DISK_BUSY_LIMIT", strValue, strErrorMessage ) == true )
|
||||
{
|
||||
int value = atoi( strValue.c_str() );
|
||||
|
||||
// 80% 이상값 설정시 오류로 표시 처리
|
||||
if( value < 0 || value > 80 )
|
||||
{
|
||||
strErrorMessage = "Config [TARGET_FHS_MAX_DISK_BUSY_LIMIT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
// double type 으로 변환 처리하여 저장.
|
||||
m_dTargetFhsMaxDiskBusyLimit = (double)( 1.0 * value );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void CProcessConfig::SetContentSelectMode( ContentSelectMode mode )
|
||||
{
|
||||
// 기존 mode 와 비교하여 서로 다른 mode 이면...
|
||||
if( m_nContentSelectMode != mode )
|
||||
{
|
||||
m_nContentSelectMode = mode;
|
||||
|
||||
// mode 변경 시간 정보 update.
|
||||
clock_gettime( CLOCK_MONOTONIC, &m_timeContentSelectModeChange );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool CProcessConfig::SetContentSelectModeToDefault()
|
||||
{
|
||||
// mode 변경 시간이 ... 지정된 시간을 초과한 경우.. default mode 로 변경 처리한다.
|
||||
if( m_nContentSelectMode != SELECT_MODE_SIZE )
|
||||
{
|
||||
struct timespec timeNow;
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeNow );
|
||||
|
||||
long timeElapsed = (long)( timeNow.tv_sec ) - (long)( m_timeContentSelectModeChange.tv_sec );
|
||||
if( timeElapsed > CONTENT_SELECT_MODE_TO_DEFAULT_TIMEOUT )
|
||||
{
|
||||
m_nContentSelectMode = SELECT_MODE_SIZE;
|
||||
|
||||
_LOG( LINF, "content select mode change to default: Big Size Order" );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/****************************************************************************
|
||||
rc_balanced config processing module
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/02/25
|
||||
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 <time.h>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
typedef enum TypeContentSelectMode {
|
||||
SELECT_MODE_SIZE = 0, // 큰 파일 우선 이동 처리
|
||||
SELECT_MODE_RANDOM // 이동 대상 content 를 random 으로 선택
|
||||
|
||||
} ContentSelectMode;
|
||||
|
||||
#define CONTENT_SELECT_MODE_TO_DEFAULT_TIMEOUT 10800 // content mode 가 default 가 아닌 경우... 다시 default 로 전환시킬 timeout 값 (sec)
|
||||
|
||||
// 본 모듈은 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_strRcId; // RC ID
|
||||
|
||||
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_nMaxQueueSize; // Max Queue Size ( default: 4096 )
|
||||
|
||||
unsigned int m_nFhsTransferDaemonPort; // FHS File Transfer Daemon (ftsd) TCP Listen Port = 14001 (default)
|
||||
|
||||
// Rebalance target FHS reselect time limit (minutes), default 30 min, use transfer total size calc, max 1440
|
||||
unsigned int m_nFhsReselectionTimeLimit;
|
||||
|
||||
unsigned int m_nHashCheckLevel; // Content 복제 및 Cache 관련 전송시 Hash Check Level 설정.
|
||||
// 0:Not check (default), 1:Partitial hash, 2:Full Hash
|
||||
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 멤버 변수 추가
|
||||
double m_dTargetFhsMaxDiskBusyLimit;
|
||||
|
||||
|
||||
// 프로세스 내부 동작 관련 변수
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
ContentSelectMode m_nContentSelectMode; // move content select mode, 상세내역은 ContentSelectMode 참고
|
||||
struct timespec m_timeContentSelectModeChange; // move content select mode 가 변경된 최종 시간.
|
||||
|
||||
|
||||
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 * GetRcId() { return m_strRcId.c_str(); }
|
||||
|
||||
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 GetMaxQueueSize() { return m_nMaxQueueSize; }
|
||||
unsigned int GetFhsTransferDaemonPort() { return m_nFhsTransferDaemonPort; }
|
||||
unsigned int GetFhsReselectionTimeLimit() { return m_nFhsReselectionTimeLimit; }
|
||||
unsigned int GetHashCheckLevel() { return m_nHashCheckLevel; }
|
||||
|
||||
double GetTargetFhsMaxDiskBusyLimit() { return m_dTargetFhsMaxDiskBusyLimit; }
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - GET/SET 함수 추가
|
||||
ContentSelectMode GetContentSelectMode() { return m_nContentSelectMode; }
|
||||
void SetContentSelectMode( ContentSelectMode mode );
|
||||
bool SetContentSelectModeToDefault();
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PROCESS_CONFIG_H__ */
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
#include "ProcessMonitoringThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessRename.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
// 정보 갱신 주기 (sec)
|
||||
// - rebalance 작업의 console 상태 정보는 2 sec 단위로 업데이트 한다.
|
||||
#define INTERVAL_UPDATE_INFO 2
|
||||
|
||||
|
||||
// 생성자
|
||||
CProcessMonitoringThread::CProcessMonitoringThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CCopyControlThread * const pCopyControlThread )
|
||||
: m_pQueueMoveStandby( pQueueMoveStandby)
|
||||
, m_pQueueUpdateStandby( pQueueUpdateStandby)
|
||||
, m_pQueueDeleteStandby( pQueueDeleteStandby )
|
||||
, m_pCopyControlThread( pCopyControlThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_mapUnlinkInfo.clear();
|
||||
pthread_mutex_init( &m_mutexUnlinkInfo, NULL );
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
CProcessMonitoringThread::~CProcessMonitoringThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
pthread_mutex_destroy( &m_mutexUnlinkInfo );
|
||||
m_mapUnlinkInfo.clear();
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueMoveStandby != NULL )
|
||||
m_pQueueMoveStandby = NULL;
|
||||
if( m_pQueueUpdateStandby != NULL )
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
if( m_pQueueDeleteStandby != NULL )
|
||||
m_pQueueDeleteStandby = NULL;
|
||||
if( m_pCopyControlThread != NULL )
|
||||
m_pCopyControlThread = NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
bool CProcessMonitoringThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueMoveStandby == NULL || m_pQueueUpdateStandby == NULL || m_pQueueDeleteStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "ProcessMonitoringThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( m_pCopyControlThread == NULL )
|
||||
{
|
||||
LOG( LERR, "ProcessMonitoringThread: copy thread object pointer not valid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CProcessMonitoringThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "ProcessMonitoringThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CProcessMonitoringThread::UpdateTitle( void )
|
||||
{
|
||||
char szTemp[100] = { 0 };
|
||||
|
||||
// 본 출력 사항에 대한 설명은 help 에 포함되어 있으므르
|
||||
// 수정할 경우 PrintUsage(), Execute() 의 내역도 수정해야 함.
|
||||
sprintf( szTemp, "%s: S%u T%d U%u D%u"
|
||||
, PROG_NAME
|
||||
, m_pQueueMoveStandby->GetSize()
|
||||
, m_pCopyControlThread->GetCopyThreadCount()
|
||||
, m_pQueueUpdateStandby->GetSize()
|
||||
, m_pQueueDeleteStandby->GetSize()
|
||||
);
|
||||
|
||||
set_ps_display( szTemp, false );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CProcessMonitoringThread::UpdateDeletedInfo( CRebalanceData & data )
|
||||
{
|
||||
// CPhysicalDeleteThread 에서
|
||||
// 정상적으로 삭제 처리한 content 에 대한 FHS, 삭제 크기 정보를 전달받는다.
|
||||
// 전달받은 정보는 내부적으로 저장했다가 주기적으로 로깅 처리한다.
|
||||
std::map<std::string, struct FhsUnlinkInfo>::iterator it;
|
||||
|
||||
pthread_mutex_lock( &m_mutexUnlinkInfo );
|
||||
it = m_mapUnlinkInfo.find( data.m_szOriginHost );
|
||||
|
||||
if( it != m_mapUnlinkInfo.end() )
|
||||
{
|
||||
// 이미 해당 FHS 가 존재하는 경우...
|
||||
// - 해당 정보를 추가로 sum 처리한다...
|
||||
it->second.hour_count += 1;
|
||||
it->second.hour_sum += data.m_uOriginContentLength;
|
||||
it->second.day_count += 1;
|
||||
it->second.day_sum += data.m_uOriginContentLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 해당 FHS 정보가 없는 경우...
|
||||
// - 신규로 추가한다.
|
||||
// - map 특성상 key 값에 의한 자동 sort 처리됨.
|
||||
struct FhsUnlinkInfo fhs;
|
||||
|
||||
fhs.hour_count = 1;
|
||||
fhs.hour_sum = data.m_uOriginContentLength;
|
||||
fhs.day_count = 1;
|
||||
fhs.day_sum = data.m_uOriginContentLength;
|
||||
|
||||
m_mapUnlinkInfo[ data.m_szOriginHost ] = fhs;
|
||||
}
|
||||
pthread_mutex_unlock( &m_mutexUnlinkInfo );
|
||||
|
||||
_LOG( LDBG, "ProcessMonitoringThread: deleted info update. [%s %s %llu]"
|
||||
, data.m_szOriginHost.c_str()
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.m_uOriginContentLength );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
|
||||
|
||||
// Thread loop 기본 함수
|
||||
void * CProcessMonitoringThread::threadFunc( void * arg )
|
||||
{
|
||||
CProcessMonitoringThread * pObject = reinterpret_cast<CProcessMonitoringThread *>( arg );
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( INTERVAL_UPDATE_INFO );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// 실제 정보 update 처리 함수
|
||||
void CProcessMonitoringThread::Execute()
|
||||
{
|
||||
char szTemp[100] = { 0 };
|
||||
|
||||
unsigned int nCountMoveStandby = m_pQueueMoveStandby->GetSize();
|
||||
unsigned int nCountUpdateStandby = m_pQueueUpdateStandby->GetSize();
|
||||
unsigned int nCountDeleteStandby = m_pQueueDeleteStandby->GetSize();
|
||||
|
||||
int nCountContentTransferThread = m_pCopyControlThread->GetCopyThreadCount();
|
||||
|
||||
int nCountMaxContentTransferThread = 0;
|
||||
|
||||
// 본 출력 사항에 대한 설명은 help 에 포함되어 있으므르
|
||||
// 수정할 경우 PrintUsage(),UpdateTitle() 의 내역도 수정해야 함.
|
||||
sprintf( szTemp, "%s: S%u T%d U%u D%u"
|
||||
, PROG_NAME
|
||||
, nCountMoveStandby
|
||||
, nCountContentTransferThread
|
||||
, nCountUpdateStandby
|
||||
, nCountDeleteStandby );
|
||||
|
||||
set_ps_display( szTemp, false );
|
||||
|
||||
if( nCountMoveStandby != 0
|
||||
|| nCountContentTransferThread != 0
|
||||
|| nCountUpdateStandby != 0
|
||||
|| nCountDeleteStandby != 0 )
|
||||
{
|
||||
nCountMaxContentTransferThread = m_pCopyControlThread->GetMaxCopyThreadCount();
|
||||
|
||||
_LOG( LINF, "[MONITOR] Move standby: %4u, Transfer thread: %d/%d, Update standby: %4u, Unlink standby: %4u"
|
||||
, nCountMoveStandby
|
||||
, nCountContentTransferThread, nCountMaxContentTransferThread
|
||||
, nCountUpdateStandby
|
||||
, nCountDeleteStandby );
|
||||
}
|
||||
|
||||
// 각 FHS 별 삭제한 통계 정보를 logging 및 초기화 처리수행.
|
||||
printUnlinkInfo();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CProcessMonitoringThread::printUnlinkInfo()
|
||||
{
|
||||
if( m_mapUnlinkInfo.empty() == true )
|
||||
return;
|
||||
|
||||
// 시간 check
|
||||
// - 시간 단위로 FHS 별 삭제한 정보를 로깅하고 초기화.
|
||||
// - 일 단위 FHS 별 삭제한 정보는 매일 10시에 로깅 및 초기화 되도록 한다.
|
||||
time_t currentTime = time( NULL );
|
||||
long currentSec = currentTime % 3600; // 1시간 = 3600 sec 로 나누어서 나머지가 현재 시간의 sec 를 나타냄
|
||||
|
||||
if( currentSec == 0 || currentSec == 1 ) // 각 시간 정각 또는 정각+1 sec
|
||||
{
|
||||
// total 정보 출력을 위한 변수
|
||||
unsigned long long uTotalFhsCount = 0;
|
||||
unsigned long long uTotalContentCount = 0;
|
||||
unsigned long long uTotalContentSum = 0;
|
||||
|
||||
std::map<std::string, struct FhsUnlinkInfo>::iterator it;;
|
||||
struct tm currentTm;
|
||||
memset( ¤tTm, 0x00, sizeof( struct tm ) );
|
||||
|
||||
localtime_r( ¤tTime, ¤tTm );
|
||||
|
||||
if( currentTm.tm_hour == 10 )
|
||||
{
|
||||
// 오전 9시 정각에는 full logging.
|
||||
pthread_mutex_lock( &m_mutexUnlinkInfo );
|
||||
|
||||
for( it = m_mapUnlinkInfo.begin(); it != m_mapUnlinkInfo.end(); it++ )
|
||||
{
|
||||
uTotalFhsCount += 1;
|
||||
uTotalContentCount += it->second.day_count;
|
||||
uTotalContentSum += it->second.day_sum;
|
||||
|
||||
// FHS 별 확보한 용량 통계 정보 출력
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance day report:: %s , count:%5lu , size:%5llu GiB."
|
||||
, it->first.c_str()
|
||||
, it->second.day_count, (it->second.day_sum)/1024/1024/1024 );
|
||||
}
|
||||
// 통계 정보 초기화
|
||||
m_mapUnlinkInfo.clear();
|
||||
pthread_mutex_unlock( &m_mutexUnlinkInfo );
|
||||
|
||||
// total report logging
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance total day report:: fhs: %llu , content: %llu , total_size: %llu GiB."
|
||||
, uTotalFhsCount
|
||||
, uTotalContentCount
|
||||
, uTotalContentSum / 1024 / 1024 / 1024 );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// 나머지는 시간당 삭제 처리한 정보 로깅
|
||||
pthread_mutex_lock( &m_mutexUnlinkInfo );
|
||||
|
||||
for( it = m_mapUnlinkInfo.begin(); it != m_mapUnlinkInfo.end(); it++ )
|
||||
{
|
||||
// 해당 시간에 작업하지 않은 FHS 는 제외
|
||||
if( it->second.hour_count > 0 )
|
||||
{
|
||||
uTotalFhsCount += 1;
|
||||
uTotalContentCount += it->second.hour_count;
|
||||
uTotalContentSum += it->second.hour_sum;
|
||||
|
||||
// FHS 별 확보한 용량 통계 정보 출력
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance hour report:: %s , count:%5lu , size:%5llu GiB."
|
||||
, it->first.c_str()
|
||||
, it->second.hour_count, (it->second.hour_sum) / 1024 / 1024 / 1024 );
|
||||
|
||||
// 각 장비 시간 단위 통계 정보 초기화
|
||||
it->second.hour_count = 0;
|
||||
it->second.hour_sum = 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock( &m_mutexUnlinkInfo );
|
||||
|
||||
|
||||
// total report logging
|
||||
_LOG( LINF, "[MONITOR] FHS rebalance total hour report:: fhs: %llu , content: %llu , total_size: %llu GiB."
|
||||
, uTotalFhsCount
|
||||
, uTotalContentCount
|
||||
, uTotalContentSum / 1024 / 1024 / 1024 );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/****************************************************************************
|
||||
rc_balanced process monitoring thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/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 __PROCESS_MONITORING_THREAD_H__
|
||||
#define __PROCESS_MONITORING_THREAD_H__
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "CopyControlThread.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
|
||||
// 각 FHS 별 unlink 처리한 정보 저장을 위한 구조체
|
||||
// map 을 통해서 관리하기 때문에 각 장비 hostname 을 정보에 포함시키지 않는다.
|
||||
struct FhsUnlinkInfo {
|
||||
|
||||
unsigned long long hour_sum; // 시간당 삭제 sum
|
||||
unsigned long hour_count; // 시간당 삭제 count
|
||||
|
||||
unsigned long long day_sum; // 일일 삭제 sum
|
||||
unsigned long day_count; // 일일 삭제 count
|
||||
};
|
||||
|
||||
|
||||
|
||||
// CProcessMonitoringThread class
|
||||
// - 프로세스 내부의 각 작업 queue count 정보를 주기적으로 console 상에 업데이트 처리
|
||||
// - 주기적으로 rebalance 처리 상황을 로깅
|
||||
class CProcessMonitoringThread
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CProcessMonitoringThread(
|
||||
CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
, CCopyControlThread * const pCopyControlThread
|
||||
);
|
||||
|
||||
// 소멸자
|
||||
~CProcessMonitoringThread();
|
||||
|
||||
|
||||
// Thread 를 생성하여 상태정보를 주기적으로 갱신, 로깅 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
// 현재 프로세스 표시 정보 직접 업데이트
|
||||
void UpdateTitle( void );
|
||||
|
||||
// PhysicalDeleteThread 모듈로 부터 정상적으로 삭제한 FHS, content 정보를 전달받기 위한 함수
|
||||
// - 전달받은 정보는 내부적으로 저장했다가 주기적으로 logging 처리
|
||||
void UpdateDeletedInfo( CRebalanceData & data );
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread Handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Move 처리할 대상을 저장하는 queueMoveStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueMoveStandby;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// unlink 처리 대상 content 정보를 저장하는 queueDeleteStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueDeleteStandby;
|
||||
|
||||
// copy control thread 객체에 대한 포인터
|
||||
CCopyControlThread * m_pCopyControlThread;
|
||||
|
||||
// unlink 처리한 FHS 별 통계 정보 저장을 위한 객체
|
||||
std::map<std::string, struct FhsUnlinkInfo> m_mapUnlinkInfo;
|
||||
pthread_mutex_t m_mutexUnlinkInfo;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 상태 정보 업데이트를 수행하는 함수
|
||||
void Execute( void );
|
||||
|
||||
// FHS 별 삭제 처리한 통계 정보를 로깅 및 초기화 처리한다.
|
||||
void printUnlinkInfo( void );
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PROCESS_MONITORING_THREAD_H__ */
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__ */
|
||||
@@ -0,0 +1,244 @@
|
||||
#include "RcdbUpdateThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Database.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 15
|
||||
#define SLEEP_TIME_MAX 30
|
||||
|
||||
|
||||
CRcdbUpdateThread::CRcdbUpdateThread( CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby )
|
||||
: m_pQueueUpdateStandby( pQueueUpdateStandby ), m_pQueueDeleteStandby( pQueueDeleteStandby )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
}
|
||||
|
||||
CRcdbUpdateThread::~CRcdbUpdateThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueUpdateStandby != NULL )
|
||||
m_pQueueUpdateStandby = NULL;
|
||||
if( m_pQueueDeleteStandby != NULL )
|
||||
m_pQueueDeleteStandby = NULL;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CRcdbUpdateThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueUpdateStandby == NULL || m_pQueueDeleteStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "CRcdbUpdateThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CRcdbUpdateThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "CRcdbUpdateThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CRcdbUpdateThread::threadFunc( void * arg )
|
||||
{
|
||||
CRcdbUpdateThread * pObject = reinterpret_cast<CRcdbUpdateThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CRcdbUpdateThread::Execute()
|
||||
{
|
||||
// RCDB update 대상 content 가 없다면.. MAX sleep 처리한다.
|
||||
if( m_pQueueUpdateStandby->GetSize() == 0 )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[UPDATE ] standby queue empty. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// update 대기 count 가 delete 대기 count 보다 많은 경우
|
||||
// delete 대기 queue 에 push 실패 발생할 가능성 있으므로.. push 실패할 상황이면.. 차라리 sleep 한다.
|
||||
|
||||
unsigned int uUpdateStandbyCount = m_pQueueUpdateStandby->GetSize();
|
||||
unsigned int uDeletePushEnableCount = m_pQueueDeleteStandby->GetMaxSize() - m_pQueueDeleteStandby->GetSize();
|
||||
if( uUpdateStandbyCount > uDeletePushEnableCount )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LDBG, "[UPDATE ] delete queue not enough (%u %u). max sleep %u sec."
|
||||
, uUpdateStandbyCount, uDeletePushEnableCount, m_uSleep );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// RCDB 처리를 수행할 객체 생성
|
||||
// RCDB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
|
||||
DataBase rcdb;
|
||||
|
||||
// RCDB 와 연결을 시도한다.
|
||||
if( rcdb.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 ] RCDB connection failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG( LINF, "[UPDATE ] start." );
|
||||
|
||||
CRebalanceData data;
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// RCDB 연결 성공시
|
||||
// - queue pop 처리하여... RCDB 업데이트 처리.
|
||||
while( m_pQueueUpdateStandby->GetSize() > 0 )
|
||||
{
|
||||
data = m_pQueueUpdateStandby->Front();
|
||||
m_pQueueUpdateStandby->Pop();
|
||||
|
||||
// COPY 처리가 성공한 content 만 RCDB 업데이트 수행
|
||||
// - 그 외에는 실패 상황이므로.. RCDB 업데이트 하면 안됨.
|
||||
if( data.m_uMoveResult == MOVE_OK )
|
||||
{
|
||||
// Query 조립
|
||||
// - deleted_yn = 'N' : 이미 삭제된 경우... COPY 처리한 파일을 삭제하기 위함
|
||||
// - get_content_length = %llu : 원본이 변경된 경우.. copy 처리한 파일을 삭제처리하기 위함
|
||||
// - uri 미사용 : uri 는 특수문자 이슈 때문에 사용하지 않고 대신 resource_id 정보를 사용함.
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "UPDATE t_meta_%s "
|
||||
"SET host_name = '%s', filename_hash = '%s' "
|
||||
"WHERE resource_id = %llu AND host_name = '%s' AND filename_hash = '%s' "
|
||||
" AND get_content_length = %llu AND deleted_yn = 'N' AND resource_type = 0 ;"
|
||||
, data.m_szServiceSeq.c_str()
|
||||
, data.m_szMoveHost.c_str(), data.m_szMoveFileNameHash.c_str()
|
||||
, data.m_uResourceId, data.m_szOriginHost.c_str(), data.m_szOriginFileNameHash.c_str()
|
||||
, data.m_uOriginContentLength );
|
||||
|
||||
// update query 수행
|
||||
rcdb.PgDoExec( szQuery );
|
||||
nResult = rcdb.GetCmdTuples();
|
||||
|
||||
_LOG( LDBG, "[UPDATE ] query result. [%d] [%s]", nResult, szQuery );
|
||||
|
||||
if( nResult == 1 ) // 적용된 row count = 1 : 정상
|
||||
{
|
||||
// RCDB 결과 Set 을 Clear 처리한다.
|
||||
rcdb.PgClear();
|
||||
|
||||
_LOG( LINF, "[UPDATE ] content meta update ok.[%s id:%llu size:%llu %s]->[%s size:%llu %s]"
|
||||
, data.m_szOriginHost.c_str(), data.m_uResourceId
|
||||
, data.m_uOriginContentLength, data.m_szOriginFileNameHash.c_str()
|
||||
, data.m_szMoveHost.c_str(), data.m_uMoveContentLength, data.m_szMoveFileNameHash.c_str() );
|
||||
}
|
||||
else if( nResult == 0 ) // 적용된 row count = 0 : 이미 삭제 또는 원본 변경된 경우... copy content 삭제하도록 설정
|
||||
{
|
||||
// RCDB 결과 Set 을 Clear 처리한다.
|
||||
rcdb.PgClear();
|
||||
|
||||
// copy content 삭제하도록 data 객체 정보 변경 처리
|
||||
data.m_uMoveResult = MOVE_ERROR_UPDATE;
|
||||
|
||||
LOG( LERR, "[UPDATE ] content meta update failed. origin modified. [%s id:%llu size:%llu %s][%s]"
|
||||
, data.m_szOriginHost.c_str(), data.m_uResourceId
|
||||
, data.m_uOriginContentLength, data.m_szOriginFileNameHash.c_str(), data.m_szURI.c_str() );
|
||||
}
|
||||
else // 그외 : resource_id 조건으로 query 실패 가능성은 없음.. DB 장애로 인지해야 할 듯.
|
||||
{
|
||||
// RCDB 결과 Set 을 Clear 처리한다.
|
||||
rcdb.PgClear();
|
||||
|
||||
// 어떤 상황인지 정확히 알 수 없으므로.. 원본, copy 본 모두 유지시킨다.
|
||||
data.m_uMoveResult = MOVE_ERROR_CREATE;
|
||||
|
||||
// 운영자가 판단하도록 로깅 처리한다.
|
||||
LOG( LERR, "[UPDATE ] content meta update result error. op check.[%d][id:%llu %s]"
|
||||
, nResult
|
||||
, data.m_uResourceId, data.m_szURI.c_str() );
|
||||
|
||||
// 더 이상 작업을 수행하지 않고 MAX sleep 처리한다.
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// delete standby queue 에 content 를 push 처리한다.
|
||||
// - 앞에서 delete queue 에 충분한 여유공간 존재하는지 check 한 후 push 하므로...
|
||||
// - push 실패는 .. 아마도... 공간 부족이 아닌 다른 문제일 가능성 존재..
|
||||
// - 우선 1번 재시도해서도 실패하면 버린다.
|
||||
if( m_pQueueDeleteStandby->Push( data ) == false )
|
||||
{
|
||||
LOG( LWAR, "[UPDATE ] delete standby queue 1st push fail.");
|
||||
sleep( 1 );
|
||||
|
||||
if( m_pQueueDeleteStandby->Push( data ) == false )
|
||||
{
|
||||
LOG( LERR, "[UPDATE ] delete standby queue push all fail. throw.");
|
||||
|
||||
// 다음번 push 작업도 실패 가능성 존재하므로....
|
||||
// 더 이상 작업하지 않고.. loop 종료하고.. 다음에 작업 진행한다.
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // while
|
||||
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/****************************************************************************
|
||||
rc_balanced content RCDB update thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/13
|
||||
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 __RCDB_UPDATE_THREAD_H__
|
||||
#define __RCDB_UPDATE_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "RcdbInfo.h"
|
||||
|
||||
|
||||
// CRcdbUpdateThread class
|
||||
// - RCDB update queue 에 들어 있는 content 를 pop 하여...
|
||||
// - RCDB 에 UPDATE 처리를 수행한 후
|
||||
// - 최종 해당 Content 를 unlink 처리를 위한 대기 queue 에 push 처리를 수행
|
||||
class CRcdbUpdateThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CRcdbUpdateThread(
|
||||
CDataQueue<CRebalanceData> * const pQueueUpdateStandby
|
||||
, CDataQueue<CRebalanceData> * const pQueueDeleteStandby
|
||||
);
|
||||
|
||||
// 소멸자
|
||||
~CRcdbUpdateThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
// FHS Copy 처리 완료 후 RCDB 업데이트 대상 content 정보를 저장하는 queueUpdateStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueUpdateStandby;
|
||||
|
||||
// unlink 처리 대상 content 정보를 저장하는 queueDeleteStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueDeleteStandby;
|
||||
|
||||
// RCDB 연결 정보 관리 객체
|
||||
CRcdbInfo m_rcdbInfo;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __RCDB_UPDATE_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "RebalanceData.h"
|
||||
|
||||
|
||||
|
||||
// 생성자
|
||||
CRebalanceData::CRebalanceData()
|
||||
{
|
||||
// 멤버 변수 초기화
|
||||
m_uOriginContentLength = 0;
|
||||
m_uMoveContentLength = 0;
|
||||
|
||||
m_uMoveResult = MOVE_OK; // Move 처리 결과는 최초에는 정상 상태로 설정 처리
|
||||
|
||||
m_uDeleteFailCount = 0;
|
||||
m_timeLastDeleteFail = 0;
|
||||
|
||||
}
|
||||
|
||||
CRebalanceData::~CRebalanceData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::string CRebalanceData::GetQueryURI()
|
||||
{
|
||||
// URI의 '(작은따옴표) 문자가 있을 경우 SQL 오류 방지를 위하여 관련 문자를 다르게 변경함
|
||||
std::string szUri = ReplaceString( m_szURI, "'", "''" );
|
||||
|
||||
return szUri;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
std::string CRebalanceData::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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/****************************************************************************
|
||||
rc_balanced data object define
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/04
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : 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 __REBALANCE_DATA_H__
|
||||
#define __REBALANCE_DATA_H__
|
||||
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
// CRebalanceData 객체의 Move 처리 결과 멤버변수인 m_uMoveResult 에 저장될 값 정의
|
||||
enum MoveResults {
|
||||
MOVE_OK = 0, // 성공 -> Origin content 삭제
|
||||
MOVE_ERROR_CREATE = 1, // 물리 파일 생성 실패 (origin 접근 실패, target FHS 선정 실패등) -> 추가 action 불필요
|
||||
MOVE_ERROR_HASHCHECK = 2, // 물리 파일 생성했으나 Hash check mismatch -> 생성된 파일 삭제 처리 필요
|
||||
MOVE_ERROR_UPDATE = 3 // 물리 파일 생성했으나 RCDB update 실패 -> 생성된 파일 삭제 처리 필요
|
||||
|
||||
};
|
||||
|
||||
|
||||
// rebalnce 처리 object data 에 대한 세부 정보를 저장하기 위한 data class
|
||||
class CRebalanceData
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CRebalanceData();
|
||||
|
||||
// 소멸자
|
||||
~CRebalanceData();
|
||||
|
||||
|
||||
// URI 정보는 특수문자 포함될 수 있어.. Query 에서 사용시 변환 처리 함수
|
||||
std::string GetQueryURI( void );
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 공통
|
||||
|
||||
unsigned long long m_uResourceId; // resouce_id 정보 (고유 key)
|
||||
std::string m_szURI; // URI 정보 (특수 문자 포함관련 query 에서 사용시 변환 필요)
|
||||
std::string m_szServiceSeq; // 서비스 sequence number
|
||||
|
||||
|
||||
// 원본 Content 정보
|
||||
|
||||
std::string m_szOriginHost; // origin hostname 정보
|
||||
std::string m_szOriginFileNameHash; // origin filename_hash
|
||||
unsigned long long m_uOriginContentLength; // origin get_content_length
|
||||
|
||||
// Move 처리 결과
|
||||
// 세부 사항은 enum MoveResults 선언 참고
|
||||
enum MoveResults m_uMoveResult;
|
||||
|
||||
|
||||
// Move Content 정보
|
||||
|
||||
std::string m_szMoveHost; // move hostname 정보
|
||||
std::string m_szMoveFileNameHash; // move filename_hash
|
||||
unsigned long long m_uMoveContentLength; // move get_content_length
|
||||
|
||||
// Delete 처리 관련 정보
|
||||
|
||||
unsigned int m_uDeleteFailCount; // delete 처리 관련 fail 횟수
|
||||
time_t m_timeLastDeleteFail; // delete 처리 관련 마지막 실패 시각
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// uri 상의 특수 문자 변경을 위한 함수
|
||||
std::string ReplaceString( const std::string& source, const std::string search, const std::string replacement );
|
||||
};
|
||||
|
||||
|
||||
#endif /* __REBALANCE_DATA_H__ */
|
||||
|
||||
@@ -0,0 +1,716 @@
|
||||
#include "ShmInfoThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Common.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "../rc_mond/ShmDefine.h"
|
||||
#include "../rc_mond/ShmControl.h"
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm> // vector sort 사용 목적
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 20
|
||||
#define SLEEP_TIME_MAX 60
|
||||
|
||||
// rc_mond shared memory 접근을 위한 key 파일.
|
||||
#define RC_MOND_SHARED_MEMORY_KEY_FILE "/user/service/rc_mond.shm"
|
||||
|
||||
// Rebalance 처리 대상 FHS 관련 FHS 의 최소 Storage 사용률 제한값.
|
||||
// 해당 percent(%) 이상인 장비만 선정되도록 하기 위함
|
||||
#define REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT 50
|
||||
|
||||
// Rebalance 처리 대상 FHS 관련 FHS 의 최대 disk busy 제한값.
|
||||
// 해당 percent(%) 이하인 장비만 선정되도록 하기 위함
|
||||
#define REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT 50.0
|
||||
|
||||
// Rebalance 대상 FHS 선정을 위한 score 계산시 disk_max_busy 항목에 부여할 가중치 정보.
|
||||
// - 점수가 높은 FHS 장비가 선정되므로...
|
||||
// - disk busy 상태인 장비는 선택되지 않도록 0.5 수준으로 설정
|
||||
// - storage_max_usage : disk_max_busy = 2 : 1 로 계산
|
||||
#define REBALANCE_TARGET_DISK_MAX_BUSY_WEIGHT 0.5
|
||||
|
||||
// Rebalance 대상 FHS 정보를 저장한 list 의 유효시간 (sec)
|
||||
// - SLEEP_TIME_MAX 값보다 커야 함.
|
||||
#define TIMEOUT_REBALANCE_LIST 120
|
||||
|
||||
// Copy 수신 가능 FHS 의 Disk 여유용량 최소값
|
||||
// - 1 TB
|
||||
#define COPY_TARGET_DISK_MIN_AVAILABLE_LIMIT (1099511627776ULL)
|
||||
|
||||
// COPY 수신 가능 FHS 선정을 위한 score 계산시 disk_max_busy 항목에 부여할 가중치 정보.
|
||||
// - 참고로 rc_mond 의 Top FHS 선정은 storage_max_usage : disk_max_busy = 1 : 4 로 계산하여 처리
|
||||
// - Copy Target 은 실제 copy 처리를 수행하는 thread 가 많지 않고...
|
||||
// - 되도록 여유 용량이 많은 장비에 copy 하는 것이 효과적이므로...
|
||||
// - storage_max_usage : disk_max_busy = 1 : 2 로 계산
|
||||
#define COPY_TARGET_DISK_MAX_BUSY_WEIGHT 2.0
|
||||
|
||||
// Copy 대상 FHS 정보를 저장한 list 의 유효시간 (sec)
|
||||
// - default sleep time 값보다 커야 함
|
||||
#define TIMEOUT_COPY_LIST 30
|
||||
|
||||
|
||||
// 공유메모리에 저장된 FHS 정보 중 적합한 FHS 정보를 찾기 위해
|
||||
// FHS 와 각 점수 정보를 저장하기 위한 구조체
|
||||
struct FhsScore {
|
||||
|
||||
char hostname[HOST_NAME_LENGTH + 1]; // hostname, NULL 포함 최대 64자까지만 허용됨.
|
||||
double score; // 계산된 score, 작을수록 idle
|
||||
};
|
||||
|
||||
|
||||
|
||||
CShmInfoThread::CShmInfoThread()
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
m_threadHandle = 0;
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
m_listRebalanceFhs.clear();
|
||||
m_listCopyTargetFhs.clear();
|
||||
|
||||
// 시간 정보 초기화.
|
||||
memset( &m_timeLastRefreshRebalanceFhs, 0x00, sizeof( struct timespec ) );
|
||||
memset( &m_timeLastRefreshCopyTargetFhs, 0x00, sizeof( struct timespec ) );
|
||||
|
||||
pthread_mutex_init( &m_mutexRebalanceFhs, NULL );
|
||||
pthread_mutex_init( &m_mutexCopyTargetFhs, NULL );
|
||||
|
||||
}
|
||||
|
||||
CShmInfoThread::~CShmInfoThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
pthread_mutex_destroy( &m_mutexRebalanceFhs );
|
||||
pthread_mutex_destroy( &m_mutexCopyTargetFhs );
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
|
||||
bool CShmInfoThread::Start()
|
||||
{
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CShmInfoThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "[SHM INF] thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CShmInfoThread::GetRebalanceTargetFhs( std::string & szResultHost, std::list<std::string>& listIgnoreFHS )
|
||||
{
|
||||
// Rebalance 대상 FHS 목록 중....
|
||||
// 전달받은 listIgnoreFHS 에 등록된 FHS 정보를 제외하고....
|
||||
// 남은 FHS 정보 중.. 맨 처음 나오는 정보를 전달한다.
|
||||
|
||||
|
||||
// 1. 저장된 list 정보가 갱신이 되지 않아 오래된 경우.... 버린다.
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
|
||||
if( currentClock.tv_sec - m_timeLastRefreshRebalanceFhs.tv_sec > TIMEOUT_REBALANCE_LIST )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] rebalance list expired. %d sec.", TIMEOUT_REBALANCE_LIST );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. 저장된 list 정보가 존재하지 않는 경우... false 반환 처리
|
||||
if( m_listRebalanceFhs.size() <= 0 )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] rebalance list empty.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<std::string>::const_iterator it;
|
||||
std::list<std::string>::const_iterator itIgnore;
|
||||
std::string szHost;
|
||||
|
||||
pthread_mutex_lock( &m_mutexRebalanceFhs );
|
||||
|
||||
for( it = m_listRebalanceFhs.begin(); it != m_listRebalanceFhs.end(); ++it )
|
||||
{
|
||||
szHost = *it;
|
||||
|
||||
// 추출된 FHS 가 ignore list 에 존재하는지 확인
|
||||
itIgnore = std::find( listIgnoreFHS.begin(), listIgnoreFHS.end(), szHost );
|
||||
if( itIgnore == listIgnoreFHS.end() )
|
||||
{
|
||||
// ignore list 에 존재하지 않는 경우...
|
||||
// 해당 장비를 선택하고.. loop 종료 처리
|
||||
szResultHost = szHost;
|
||||
pthread_mutex_unlock( &m_mutexRebalanceFhs );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] proper rebalance fhs [%s] selected.",szResultHost.c_str() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 적합한 장비를 찾지 못한 경우.
|
||||
pthread_mutex_unlock( &m_mutexRebalanceFhs );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] proper rebalance fhs not selectd. total[%lu], ignore[%lu]."
|
||||
, m_listRebalanceFhs.size(), listIgnoreFHS.size() );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy content 를 저장할 target FHS 정보를 반환
|
||||
// 2021-01-07 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - 복사 가능 FHS 가 존재하는지 검색 관련 오류 발생시 반환값 정보를 좀더 세분화 처리
|
||||
// - 정상 처리시 : return 1
|
||||
// - 복사 가능 물리적 FHS 가 없는 경우 : return -1
|
||||
// - 복사 가능 물리 FHS 는 존재하지만 ignore 목록에 포함되어.. 실제 복사 가능 FHS 가 없는 경우 : return 0
|
||||
int CShmInfoThread::GetCopyTargetFhs( std::string & szResultHost, std::list<std::string>& listIgnoreFHS )
|
||||
{
|
||||
// COPY 수신 가능 FHS list 에서
|
||||
// 전달받은 Ignore FHS 에 등록된 FHS 를 제외하고
|
||||
// 남은 FHS 중 맨 처음 나오는 정보를 전달한다.
|
||||
|
||||
// 1. 저장된 list 정보가 갱신이 되지 않아 오래된 경우.... 버린다.
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
|
||||
if( currentClock.tv_sec - m_timeLastRefreshCopyTargetFhs.tv_sec > TIMEOUT_COPY_LIST )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] copy target info list expired. %d sec.", TIMEOUT_COPY_LIST );
|
||||
|
||||
// 복사 가능 물리 FHS 가 아예 없는 경우... -1 return
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 2. 저장된 list 정보가 존재하지 않는 경우
|
||||
if( m_listCopyTargetFhs.size() <= 0 )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] ] copy target info list empty." );
|
||||
|
||||
// 복사 가능 물리 FHS 가 아예 없는 경우... -1 return
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
std::list<std::string>::const_iterator it;
|
||||
std::list<std::string>::const_iterator itIgnore;
|
||||
std::string szHost;
|
||||
|
||||
pthread_mutex_lock( &m_mutexCopyTargetFhs );
|
||||
|
||||
for( it = m_listCopyTargetFhs.begin(); it != m_listCopyTargetFhs.end(); ++it )
|
||||
{
|
||||
szHost = *it;
|
||||
|
||||
// 추출된 FHS 가 ignore list 에 존재하는지 확인
|
||||
itIgnore = std::find( listIgnoreFHS.begin(), listIgnoreFHS.end(), szHost );
|
||||
if( itIgnore == listIgnoreFHS.end() )
|
||||
{
|
||||
// ignore list 에 존재하지 않는 경우...
|
||||
// 해당 장비를 선택하고.. loop 종료 처리
|
||||
szResultHost = szHost;
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] proper copy target [%s] selected.", szResultHost.c_str() );
|
||||
|
||||
// 복사 가능 물리 FHS 가 존재하는 경우 ... 1 return
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 적합한 장비를 찾지 못한 경우.
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
LOG( LWAR, "[SHM INF] proper copy target fhs not selectd. total[%lu], ignore[%lu]."
|
||||
, m_listCopyTargetFhs.size(), listIgnoreFHS.size() );
|
||||
|
||||
// 복사 가능 물리 FHS 가 존재하지만.. ignore 에 포함되어.. 실제 가능 FHS 가 전혀 없는 경우 ... 0 return
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CShmInfoThread::IsValidCopyTargetFhs( void )
|
||||
{
|
||||
// 1. 저장된 list 정보가 갱신이 되지 않아 오래되었는지 check
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
|
||||
bool bResult = true;
|
||||
|
||||
pthread_mutex_lock( &m_mutexCopyTargetFhs );
|
||||
|
||||
if( currentClock.tv_sec - m_timeLastRefreshCopyTargetFhs.tv_sec > TIMEOUT_COPY_LIST )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] copy target list expired. %d sec.", TIMEOUT_COPY_LIST );
|
||||
bResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( m_listCopyTargetFhs.empty() == true )
|
||||
{
|
||||
LOG( LWAR, "[SHM INF] copy target list empty." );
|
||||
bResult = false;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
|
||||
|
||||
void * CShmInfoThread::threadFunc( void * arg )
|
||||
{
|
||||
CShmInfoThread * pObject = reinterpret_cast<CShmInfoThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CShmInfoThread::Execute()
|
||||
{
|
||||
// 다음 sleep time 은 default 로 처리
|
||||
// - 하위 flow 에서 문제 발생하면 sleep time 을 max 로 조정
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// rc_mond 가 기동 중인지 check
|
||||
// - 미기동 상태이면... 기동시까지 대기.
|
||||
if( IsRunRcMonD() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s process not running. max sleep %u sec.", PROCESS_NAME_RC_MONITORING_DAEMON, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 공유메모리 접근
|
||||
key_t iShmKey;
|
||||
|
||||
// 공유메모리 key 생성을 위한 임시 파일 생성 처리.
|
||||
FILE * pShmFile = fopen( RC_MOND_SHARED_MEMORY_KEY_FILE, "r" );
|
||||
|
||||
if( pShmFile == NULL )
|
||||
{
|
||||
// 공유메모리 접근 key file 존재하지 않는 경우....
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s shared memory key file(%s) not exist. max sleep %u sec."
|
||||
, PROCESS_NAME_RC_MONITORING_DAEMON , RC_MOND_SHARED_MEMORY_KEY_FILE , m_uSleep );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 공유메모리 접근 key file 존재하는 경우
|
||||
fclose( pShmFile );
|
||||
pShmFile = NULL;
|
||||
}
|
||||
|
||||
// 공유메모리 접근 key 값 추출
|
||||
iShmKey = ftok( RC_MOND_SHARED_MEMORY_KEY_FILE, 1 );
|
||||
if( iShmKey == (key_t)-1 )
|
||||
{
|
||||
// 공유메모리 접근 key 획득 실패시
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s shared memory key generate fail with file(%s). max sleep %u sec."
|
||||
, PROCESS_NAME_RC_MONITORING_DAEMON , RC_MOND_SHARED_MEMORY_KEY_FILE , m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 공유메모리 attach 처리
|
||||
CShmControl< struct RcInfo > shmCtl;
|
||||
if( shmCtl.Attach( iShmKey, sizeof( struct RcInfo ) ) == false )
|
||||
{
|
||||
// 공유메모리 접근 실패시
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SHM INF] %s shared memory attach fail with key(%d). max sleep %u sec."
|
||||
, PROCESS_NAME_RC_MONITORING_DAEMON, iShmKey, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
_LOG( LDBG, "[SHM INF] start." );
|
||||
|
||||
// 공유메모리 최상위 접근 포인터 획득
|
||||
struct RcInfo * pShm = shmCtl.GetShmPtr();
|
||||
|
||||
// copy 처리 수신 가능 FHS 추출 및 rebalance 대상 FHS 추출 목록 update
|
||||
if( RefreshFhsList( pShm ) == false )
|
||||
{
|
||||
// 실패 발생시 다음 sleep time 최대 설정.
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
}
|
||||
|
||||
// 공유메모리 접근 해제
|
||||
shmCtl.Detach();
|
||||
pShm = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool FhsScoreSortASC( const struct FhsScore & fhs1, const struct FhsScore & fhs2 )
|
||||
{
|
||||
return (fhs1.score < fhs2.score);
|
||||
}
|
||||
|
||||
bool FhsScoreSortDESC( const struct FhsScore & fhs1, const struct FhsScore & fhs2 )
|
||||
{
|
||||
return (fhs1.score > fhs2.score);
|
||||
}
|
||||
|
||||
|
||||
bool CShmInfoThread::RefreshFhsList( const struct RcInfo * const pShm )
|
||||
{
|
||||
// FHS 의 시스템 정보 업데이트가 오래된 경우..
|
||||
// - rc_mond 에서 지정된 시간( 20 sec ) 를 초과한 경우.. expire 처리하지만...
|
||||
// - 오류가 발생할 수 있고... FHS 상태가 이상할 경우.. 해당 장비는 선택하지 않는 것이 좋으므로...
|
||||
// - 업데이트가 비교적 최근에 발생한 FHS 정보만을 사용토록 한다.
|
||||
// - csagentd 는 4(service), 10(system) sec 마다 정보 전송...
|
||||
// - 따라서.. 8 sec 초과된 정보는 사용하지 않도록 한다.
|
||||
// - local time 은 시간 동기화 영향 받으므로... update_time_clock_sec 항목을 사용하여 판단한다.
|
||||
time_t expireTime;
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
expireTime = currentClock.tv_sec - 8;
|
||||
|
||||
|
||||
// 1. Write 가능 FHS 의 Storage 사용량 정보를 조회하여 평균을 구한다.
|
||||
// - 전체 FHS 의 평균을 구하면.. 용량이 충분한데 장비 문제로 강제 업로드 막은 장비까지 계산되어
|
||||
// - 평균을 낮추 버리는 문제가 발생하여...
|
||||
// - reblance 수신 FHS 가 다시 rebalance 처리 대상 FHS 가 될 수 있는 문제가 생긴다.
|
||||
const struct ClientInfo * pClient = NULL;
|
||||
unsigned int uFhsCount = 0;
|
||||
unsigned int uFhsTotalUsage = 0;
|
||||
int index = 0;
|
||||
unsigned int uFhsAverageUsage = 0;
|
||||
|
||||
for( index = 0; index <= pShm->client_max_index; index++ )
|
||||
{
|
||||
pClient = &(pShm->client[index]);
|
||||
|
||||
// slot 할당되어 있고.. data 가 유효하고..
|
||||
// write 가능 정상 상태 이고
|
||||
// system 상태 정보가 정상인 경우...( system 정보가 늦게 수신되므로.. 이를 반영 )
|
||||
// service 상태 정보가 expire 시간보다 오래되지 않은 최근 업데이트된 FHS 인 경우..
|
||||
if( pClient->use == 1
|
||||
&& pClient->valid == 1
|
||||
&& pClient->service.service_stat == SERVICE_STAT_OK
|
||||
&& pClient->system.update_time != 0
|
||||
&& pClient->service.update_time_clock_sec >= expireTime
|
||||
)
|
||||
{
|
||||
// 평균값 계산을 위한 값 처리
|
||||
uFhsTotalUsage += pClient->system.storage_max_usage;
|
||||
uFhsCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// write 가능 유효한 FHS 가 없는 경우
|
||||
if( uFhsCount < 1 )
|
||||
{
|
||||
_LOG( LWAR, "[SHM INF] starge average usage calc fail: write available fhs[%u] not exist.", uFhsCount );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Storage 사용량 평균값 계산
|
||||
uFhsAverageUsage = (uFhsTotalUsage / uFhsCount) + 1; // 소수점 짤려서.. 86.7% 여도 86 % 로 계산되므로.. 1 보정
|
||||
_LOG( LINF, "[SHM INF] starge average usage calc ok. write fhs count[%u], avg[%u %%]", uFhsCount, uFhsAverageUsage );
|
||||
|
||||
// 2. 평균 Storage 사용률 미만인 FHS 를 Copy 수신 FHS 로 선정한다.
|
||||
// - 만약 적합한 FHS 가 없는 경우 아래의 rebalance 대상 FHS 로 선정하지 않는다.
|
||||
if( RefreshCopyTargetFhsList( pShm, uFhsAverageUsage ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. rebalance 대상 FHS 로 선정한다.
|
||||
// - 평균 Storage 사용률이 70% 미만인 경우... 평균 Storage 사용률보다 3% 이상 높은 FHS 를 rebalance 대상 FHS 로 선정
|
||||
// - 평균 Storage 사용률이 70% 이상인 경우.. 평균 Storage 사용률보다 1% 이상 높은 FHS 를 rebalance 대상 FHS 로 설정
|
||||
unsigned int uRebalanceTargetStorageUsage = 0;
|
||||
if( uFhsAverageUsage < 70 )
|
||||
{
|
||||
uRebalanceTargetStorageUsage = uFhsAverageUsage + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
uRebalanceTargetStorageUsage = uFhsAverageUsage + 1;
|
||||
}
|
||||
|
||||
if( RefreshRebalanceFhsList( pShm, uRebalanceTargetStorageUsage ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CShmInfoThread::RefreshCopyTargetFhsList( const RcInfo * const pShm, unsigned int uStorageMaxUsage )
|
||||
{
|
||||
// FHS 의 시스템 정보 업데이트가 오래된 경우..
|
||||
// - rc_mond 에서 지정된 시간( 20 sec ) 를 초과한 경우.. expire 처리하지만...
|
||||
// - 오류가 발생할 수 있고... FHS 상태가 이상할 경우.. 해당 장비는 선택하지 않는 것이 좋으므로...
|
||||
// - 업데이트가 비교적 최근에 발생한 FHS 정보만을 사용토록 한다.
|
||||
// - csagentd 는 4(service), 10(system) sec 마다 정보 전송...
|
||||
// - 따라서.. 8 sec 초과된 정보는 사용하지 않도록 한다.
|
||||
// - local time 은 시간 동기화 영향 받으므로... update_time_clock_sec 항목을 사용하여 판단한다.
|
||||
time_t expireTime;
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
expireTime = currentClock.tv_sec - 8;
|
||||
|
||||
const struct ClientInfo * pClient = NULL;
|
||||
struct FhsScore stFhsScore;
|
||||
std::vector<struct FhsScore> vecFhsRank;
|
||||
int nFhsCount = 0;
|
||||
|
||||
vecFhsRank.clear();
|
||||
|
||||
// 2021-01-19 NEW huibong move 대상 FHS 선정시 disk busy 허용 max 값 추가 (#33395)
|
||||
// - conf 처리 관련 변수 사용하도록 수정 처리
|
||||
double dTargetFhsMaxDiskBusyLimit = CProcessConfig::GetInstance()->GetTargetFhsMaxDiskBusyLimit();
|
||||
|
||||
// write 가능 장비를 찾아.. score 계산.
|
||||
for( int i = 0; i <= pShm->client_max_index; i++ )
|
||||
{
|
||||
pClient = &(pShm->client[i]);
|
||||
|
||||
// slot 할당되어 있고.. data 가 유효하고..
|
||||
// write 가능 상태
|
||||
// system 상태 정보가 정상인 경우...( system 정보가 늦게 수신되므로.. 이를 반영 )
|
||||
// service 상태 정보가 expire 시간보다 오래되지 않은 최근 업데이트된 FHS 인 경우..
|
||||
// disk 최대 사용율이 평균값 미만
|
||||
// disk busy 하지 않고.. 남은 유효 용량 역시 충분한 경우
|
||||
if( pClient->use == 1
|
||||
&& pClient->valid == 1
|
||||
&& pClient->service.service_stat == SERVICE_STAT_OK
|
||||
&& pClient->system.update_time != 0
|
||||
&& pClient->service.update_time_clock_sec >= expireTime
|
||||
&& pClient->system.storage_max_usage < (int)uStorageMaxUsage
|
||||
&& pClient->system.disk_max_busy <= dTargetFhsMaxDiskBusyLimit
|
||||
&& pClient->system.storage_avail >= COPY_TARGET_DISK_MIN_AVAILABLE_LIMIT
|
||||
)
|
||||
{
|
||||
// 임시 객체 초기화.
|
||||
memset( &stFhsScore, 0x00, sizeof( struct FhsScore ) );
|
||||
|
||||
// hostname 저장.
|
||||
strncpy( stFhsScore.hostname, pClient->hostname, HOST_NAME_LENGTH );
|
||||
|
||||
// write score 계산 처리.
|
||||
// - storage_max_usage 항목과
|
||||
// - disk_max_busy * 전달받은 가중치 값의 합.
|
||||
stFhsScore.score = (double)(pClient->system.storage_max_usage)
|
||||
+ ((pClient->system.disk_max_busy) * COPY_TARGET_DISK_MAX_BUSY_WEIGHT);
|
||||
|
||||
// vector 에 결과 저장
|
||||
vecFhsRank.push_back( stFhsScore );
|
||||
|
||||
// count
|
||||
nFhsCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// copy write 가능 장비가 존재하면
|
||||
if( nFhsCount > 0 )
|
||||
{
|
||||
if( nFhsCount > 1 )
|
||||
{
|
||||
// 가장 점수가 낮아 이상적인 장비 순서로 정렬
|
||||
std::sort( vecFhsRank.begin(), vecFhsRank.end(), FhsScoreSortASC );
|
||||
}
|
||||
|
||||
// lock 을 걸고...
|
||||
// m_listCopyTargetFhs 정보 및 m_timeLastRefreshCopyTargetFhs 정보 업데이트.
|
||||
std::vector<struct FhsScore>::iterator it;
|
||||
std::string szRefresList;
|
||||
|
||||
// 기존 list 에 저장된 정보 clear
|
||||
pthread_mutex_lock( &m_mutexCopyTargetFhs );
|
||||
m_listCopyTargetFhs.clear();
|
||||
|
||||
for( it = vecFhsRank.begin(); it != vecFhsRank.end(); ++it )
|
||||
{
|
||||
// 정렬된 순서대로 list 에 push 처리
|
||||
m_listCopyTargetFhs.push_back( it->hostname );
|
||||
|
||||
szRefresList.append( it->hostname );
|
||||
szRefresList.append( " " );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] copy target fhs list push_back.[%s].", it->hostname );
|
||||
}
|
||||
|
||||
// refresh time 정보 업데이트
|
||||
clock_gettime( CLOCK_MONOTONIC, &m_timeLastRefreshCopyTargetFhs );
|
||||
pthread_mutex_unlock( &m_mutexCopyTargetFhs );
|
||||
|
||||
_LOG( LINF, "[SHM INF] copy target fhs list refreshed. usage avg limit[%u %%]. [%d][%s]"
|
||||
, uStorageMaxUsage, nFhsCount, szRefresList.c_str() );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy 수신 가능 FHS 가 없는 경우...
|
||||
// - 관련 내역 로깅
|
||||
_LOG( LWAR, "[SHM INF] copy target fhs not exist. usage avg limit[%u %%], busy limit[%.1f %%]"
|
||||
, uStorageMaxUsage, dTargetFhsMaxDiskBusyLimit );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CShmInfoThread::RefreshRebalanceFhsList( const RcInfo * const pShm, unsigned int uStorageMinUsage )
|
||||
{
|
||||
// FHS 의 시스템 정보 업데이트가 오래된 경우..
|
||||
// - rc_mond 에서 지정된 시간( 20 sec ) 를 초과한 경우.. expire 처리하지만...
|
||||
// - 오류가 발생할 수 있고... FHS 상태가 이상할 경우.. 해당 장비는 선택하지 않는 것이 좋으므로...
|
||||
// - 업데이트가 비교적 최근에 발생한 FHS 정보만을 사용토록 한다.
|
||||
// - csagentd 는 4(service), 10(system) sec 마다 정보 전송...
|
||||
// - 따라서.. 8 sec 초과된 정보는 사용하지 않도록 한다.
|
||||
// - local time 은 시간 동기화 영향 받으므로... update_time_clock_sec 항목을 사용하여 판단한다.
|
||||
time_t expireTime;
|
||||
struct timespec currentClock;
|
||||
memset( ¤tClock, 0x00, sizeof( struct timespec ) );
|
||||
clock_gettime( CLOCK_MONOTONIC, ¤tClock );
|
||||
expireTime = currentClock.tv_sec - 8;
|
||||
|
||||
const struct ClientInfo * pClient = NULL;
|
||||
struct FhsScore stFhsScore;
|
||||
std::vector<struct FhsScore> vecFhsRank;
|
||||
int nFhsCount = 0;
|
||||
|
||||
vecFhsRank.clear();
|
||||
|
||||
// rebalance 대상 장비를 찾아.. score 계산.
|
||||
for( int i = 0; i <= pShm->client_max_index; i++ )
|
||||
{
|
||||
pClient = &(pShm->client[i]);
|
||||
|
||||
// slot 할당되어 있고.. data 가 유효하고..
|
||||
// read only 상태인 경우..
|
||||
// system 상태 정보가 정상인 경우...( system 정보가 늦게 수신되므로.. 이를 반영 )
|
||||
// service 상태 정보가 expire 시간보다 오래되지 않은 최근 업데이트된 FHS 인 경우..
|
||||
// disk 최대 사용율이 전달받은 값 이상이고
|
||||
// disk 최대 사용량이 REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT(%) 이상인 장비
|
||||
// disk busy 가 REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT (%) 이하인 장비.
|
||||
if( pClient->use == 1
|
||||
&& pClient->valid == 1
|
||||
&& ( pClient->service.service_stat == SERVICE_STAT_RONLY || pClient->service.service_stat == SERVICE_STAT_OK )
|
||||
&& pClient->system.update_time != 0
|
||||
&& pClient->service.update_time_clock_sec >= expireTime
|
||||
&& pClient->system.storage_max_usage >= (int)uStorageMinUsage
|
||||
&& pClient->system.storage_max_usage >= REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT
|
||||
&& pClient->system.disk_max_busy <= REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT )
|
||||
{
|
||||
|
||||
// 임시 객체 초기화.
|
||||
memset( &stFhsScore, 0x00, sizeof( struct FhsScore ) );
|
||||
|
||||
// hostname 저장.
|
||||
strncpy( stFhsScore.hostname, pClient->hostname, HOST_NAME_LENGTH );
|
||||
|
||||
// score 계산 처리.
|
||||
// - storage_max_usage 항목에서 disk_max_busy 값을 빼서 점수 계산
|
||||
stFhsScore.score = (double)(pClient->system.storage_max_usage)
|
||||
- ((pClient->system.disk_max_busy) * REBALANCE_TARGET_DISK_MAX_BUSY_WEIGHT);
|
||||
|
||||
// vector 에 결과 저장
|
||||
vecFhsRank.push_back( stFhsScore );
|
||||
|
||||
// result count 증가.
|
||||
nFhsCount += 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Rebalance 대상 장비가 존재하면
|
||||
if( nFhsCount > 0 )
|
||||
{
|
||||
if( nFhsCount > 1 )
|
||||
{
|
||||
// 가장 점수가 높은 순으로 정렬
|
||||
std::sort( vecFhsRank.begin(), vecFhsRank.end(), FhsScoreSortDESC );
|
||||
}
|
||||
|
||||
std::string szRefresList;
|
||||
|
||||
// lock 을 걸고...
|
||||
// m_listCopyTargetFhs 정보 및 m_timeLastRefreshCopyTargetFhs 정보 업데이트.
|
||||
std::vector<struct FhsScore>::iterator it;
|
||||
|
||||
// 기존 list 에 저장된 정보 clear
|
||||
pthread_mutex_lock( &m_mutexRebalanceFhs );
|
||||
m_listRebalanceFhs.clear();
|
||||
|
||||
for( it = vecFhsRank.begin(); it != vecFhsRank.end(); ++it )
|
||||
{
|
||||
// 정렬된 순서대로 list 에 push 처리
|
||||
m_listRebalanceFhs.push_back( it->hostname );
|
||||
|
||||
szRefresList.append( it->hostname );
|
||||
szRefresList.append( " " );
|
||||
|
||||
_LOG( LDBG, "[SHM INF] rebalance fhs list push_back.[%s]", it->hostname );
|
||||
}
|
||||
|
||||
// refresh time 정보 업데이트
|
||||
clock_gettime( CLOCK_MONOTONIC, &m_timeLastRefreshRebalanceFhs );
|
||||
pthread_mutex_unlock( &m_mutexRebalanceFhs );
|
||||
|
||||
_LOG( LINF, "[SHM INF] rebalance fhs list refreshed. usage limit[%u %%]. [%d][%s]"
|
||||
, (uStorageMinUsage > REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT ? uStorageMinUsage : REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT)
|
||||
, nFhsCount, szRefresList.c_str() );
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rebalance 처리 대상 FHS 가 없는 경우...
|
||||
// - 관련 내역 로깅
|
||||
_LOG( LINF, "[SHM INF] rebalance fhs not exist with usage limit [%u %%], busy limit [%.2f %%]"
|
||||
, (uStorageMinUsage > REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT ? uStorageMinUsage : REBALANCE_TARGET_DISK_MIN_USAGE_LIMIT)
|
||||
, REBALANCE_TARGET_DISK_MAX_BUSY_LIMIT );
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
rc_balanced shared memory info header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/26
|
||||
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 __SHM_INFO_THREAD_H__
|
||||
#define __SHM_INFO_THREAD_H__
|
||||
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
|
||||
// CShmInfoThread class
|
||||
// - rc_mond 공유 메모리에 접근하여....
|
||||
// - 주기적으로 rebalance 처리할 FHS 정보를 조회하여 저장
|
||||
// - 주기적으로 Copy 수신 가능한 FHS 정보를 조회하여 저장
|
||||
// - 작업 처리 관련 각 모듈에서 필요한 정보 제공을 위한 인터페이스 제공 목적
|
||||
class CShmInfoThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CShmInfoThread();
|
||||
|
||||
// 소멸자
|
||||
~CShmInfoThread();
|
||||
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리
|
||||
bool Start( void );
|
||||
|
||||
// Rebalance 대상 FHS 정보를 반환
|
||||
bool GetRebalanceTargetFhs( std::string & szResultHost, std::list<std::string> & listIgnoreFHS );
|
||||
|
||||
// copy content 를 저장할 target FHS 정보를 반환
|
||||
int GetCopyTargetFhs( std::string & szResultHost, std::list<std::string> & listIgnoreFHS );
|
||||
|
||||
// Copy receive target FHS 정보가 유효한지 check 하기 위한 함수.
|
||||
bool IsValidCopyTargetFhs( void );
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread loop 마다 sleep time 처리 관련 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
std::list<std::string> m_listRebalanceFhs; // Rebalance 대상 FHS 정보를 저장할 list
|
||||
struct timespec m_timeLastRefreshRebalanceFhs; // m_listRebalanceFhs 의 마지막 refresh 시간 정보를 저장할 변수
|
||||
pthread_mutex_t m_mutexRebalanceFhs; // m_listRebalanceFhs 관련 lock 객체
|
||||
|
||||
|
||||
std::list<std::string> m_listCopyTargetFhs; // Copy 수신 처리 가능 FHS 정보를 저장할 list
|
||||
struct timespec m_timeLastRefreshCopyTargetFhs; // m_listCopyTargetFhs 의 마지막 refresh 시간 정보를 저장할 변수
|
||||
pthread_mutex_t m_mutexCopyTargetFhs; // m_listCopyTargetFhs 관련 lock 객체
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void *arg );
|
||||
|
||||
// 실제 정보 갱신을 처리하는 함수
|
||||
void Execute( void );
|
||||
|
||||
|
||||
// rc_mond 공유메모리에서 rebalance 대상 및 Copy 수신 대상 FHS 목록을 갱신 처리하는 함수
|
||||
bool RefreshFhsList( const struct RcInfo * const pShm );
|
||||
|
||||
// rc_mond 공유메모리에서 Copy 처리 수신 가능 FHS 목록을 갱신 처리하는 함수
|
||||
// - 전달받은 Storage Max usge 값 미만인 FHS 만 선정.
|
||||
bool RefreshCopyTargetFhsList( const struct RcInfo * const pShm, unsigned int uStorageMaxUsage );
|
||||
|
||||
// rc_mond 공유메모리에서 rebalance 대상 FHS 목록을 갱신 처리하는 함수
|
||||
// - 전달받은 Storage Min usge 값 이상인 FHS 만 선정.
|
||||
bool RefreshRebalanceFhsList( const struct RcInfo * const pShm, unsigned int uStorageMinUsage );
|
||||
|
||||
};
|
||||
|
||||
#endif /* __SHM_INFO_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,630 @@
|
||||
#include "TargetSelectThread.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "Common.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
|
||||
#define SLEEP_TIME_DEFAULT 30
|
||||
#define SLEEP_TIME_MAX 60
|
||||
|
||||
|
||||
// Content 조회시 최대 조회 limit
|
||||
#define CONTENT_SELECT_MAX_LIMIT_COUNT 100
|
||||
|
||||
|
||||
// Content select query 수행 시간이 지정된 시간 이상일 경우 로깅 처리 (sec)
|
||||
#define LOGGING_MAX_TIME_QUERY_EXEC 60
|
||||
|
||||
|
||||
// m_pQueueMoveStandby queue 에 추가 push 를 방지를 제한값.
|
||||
// - Max Copy Thread 값에 2배수까지 허용한다.
|
||||
#define MOVE_STANDBY_QUEUE_PUSH_HIGH_LIMIT (MAX_COPY_THREAD_COUNT * 2)
|
||||
|
||||
|
||||
|
||||
CTargetSelectThread::CTargetSelectThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CShmInfoThread * const pShmInfoThread )
|
||||
: m_pQueueMoveStandby( pQueueMoveStandby), m_pShmInfoThread( pShmInfoThread )
|
||||
{
|
||||
// 멤버변수 초기화
|
||||
|
||||
m_threadHandle = 0; // thread handle 초기화
|
||||
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
m_listCompleteFhs.clear();
|
||||
|
||||
m_uTimeoutFhsReselection = 0;
|
||||
m_uMaxTransferlimit = 0;
|
||||
}
|
||||
|
||||
CTargetSelectThread::~CTargetSelectThread()
|
||||
{
|
||||
// thread 가 동작 중인 경우.. thread 중지 처리
|
||||
if( m_threadHandle != 0 )
|
||||
{
|
||||
pthread_cancel( m_threadHandle );
|
||||
}
|
||||
|
||||
sleep( 1 ); // thread 종료 잠시 대기
|
||||
|
||||
m_listCompleteFhs.clear();
|
||||
|
||||
// 혹시나 해서 정석대로 NULL 처리 추가함.
|
||||
if( m_pQueueMoveStandby != NULL )
|
||||
m_pQueueMoveStandby = NULL;
|
||||
if( m_pShmInfoThread != NULL )
|
||||
m_pShmInfoThread = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
bool CTargetSelectThread::Start()
|
||||
{
|
||||
// 멤버 변수 유효성 check
|
||||
if( m_pQueueMoveStandby == NULL )
|
||||
{
|
||||
LOG( LERR, "TargetSelectThread: queue object pointer not valid." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// 전송 처리 관련 내부 변수 계산.
|
||||
CalcInternalLimit();
|
||||
|
||||
// RCDB 접속 정보 load
|
||||
m_rcdbInfo.Load();
|
||||
|
||||
// 작업 thread 생성
|
||||
int nResult = pthread_create( &m_threadHandle, NULL, CTargetSelectThread::threadFunc, this );
|
||||
if( nResult != 0 )
|
||||
{
|
||||
// thread 생성 실패
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "TargetSelectThread: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void * CTargetSelectThread::threadFunc( void * arg )
|
||||
{
|
||||
CTargetSelectThread * pObject = reinterpret_cast<CTargetSelectThread *>(arg);
|
||||
pthread_detach( pthread_self() );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
pthread_testcancel();
|
||||
pObject->Execute();
|
||||
pthread_testcancel();
|
||||
|
||||
// 지정된 주기로 실행
|
||||
sleep( pObject->m_uSleep );
|
||||
}
|
||||
|
||||
// thread 종료시 handle 정보 초기화
|
||||
pObject->m_threadHandle = 0;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CTargetSelectThread::Execute()
|
||||
{
|
||||
// sleep time 조정 처리
|
||||
m_uSleep = SLEEP_TIME_DEFAULT;
|
||||
|
||||
// rc_mond 가 기동 중인지 check
|
||||
// - 미기동 상태이면... 기동시까지 대기.
|
||||
if( IsRunRcMonD() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LERR, "[SELECT ] %s process not running. max sleep %u sec.", PROCESS_NAME_RC_MONITORING_DAEMON, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// MOVE 처리 가능한 FHS 존재 여부 check
|
||||
// - 존재하지 않는 경우.. content select 해서 push 해 놓기만 하다보면....
|
||||
// - 결국 중복 content push 되는 상황 발생할 수 있으므로....
|
||||
// - MOVE 수신 가능한 FHS 가 존재하지 않은 경우... content select 되지 않도록 한다.
|
||||
if( m_pShmInfoThread->IsValidCopyTargetFhs() == false )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] valid copy target fhs not exist. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// Move 처리 대상 content 정보를 저장하는 queue 가 특정값 이상이면
|
||||
// sleep time 을 조정하고.. 이번 에는 작업을 수행하지 않는다.
|
||||
// copy 처리 thread 수가 작아서... 병목 구간
|
||||
// 이 때 많은 양을 push 처리하며... 중복 발생 가능성이 높아짐
|
||||
// 따라서 해당 queue 가 지정된 값 이하인 경우에만 rebalance 대상을 선택 처리한다.
|
||||
|
||||
if( m_pQueueMoveStandby->GetSize() > MOVE_STANDBY_QUEUE_PUSH_HIGH_LIMIT )
|
||||
{
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] move queue count high [%u / %d]. job skip. max sleep %u sec."
|
||||
, m_pQueueMoveStandby->GetSize(), MOVE_STANDBY_QUEUE_PUSH_HIGH_LIMIT, m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 이미 선택되어 처리된 FHS 목록 중 expire 된 항목 제거하여 갱신 처리
|
||||
UpdateCompleteFhs();
|
||||
|
||||
// 재선택 방지를 위한 ignore fhs list 생성
|
||||
std::list<std::string> listIgnoreFHS;
|
||||
std::list<CCompleteFhsInfo>::iterator it;
|
||||
|
||||
listIgnoreFHS.clear();
|
||||
|
||||
for( it = m_listCompleteFhs.begin(); it != m_listCompleteFhs.end(); ++it )
|
||||
{
|
||||
listIgnoreFHS.push_back( it->m_szHostName );
|
||||
}
|
||||
|
||||
|
||||
// m_pShmInfoThread 객체를 이용하여 rebalance 처리할 FHS 목록을 가져온다.
|
||||
std::string szRebalanceHost;
|
||||
if( m_pShmInfoThread->GetRebalanceTargetFhs( szRebalanceHost, listIgnoreFHS ) == false )
|
||||
{
|
||||
// rebalance 대상 host 를 선정하지 못한 경우...
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
|
||||
_LOG( LINF, "[SELECT ] reblance fhs not selected. max sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// RCDB 처리를 수행할 객체 생성
|
||||
// RCDB 와 연결은 필요시마다 연결 후 해제 처리한다. (세션 유지 않도록...)
|
||||
DataBase rcdb;
|
||||
|
||||
// RCDB 와 연결을 시도한다.
|
||||
if( rcdb.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, "TargetSelectThread: RCDB connection failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
}
|
||||
|
||||
// 최종 선정된 FHS 에 대해 MOVE 처리할 최종 서비스를 선택한다.
|
||||
std::string szService;
|
||||
if( SelectService( rcdb, szService ) == false )
|
||||
{
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
// 서비스 선택 실패시...
|
||||
m_uSleep = SLEEP_TIME_MAX;
|
||||
LOG( LERR, "TargetSelectThread: Target service select failed. sleep %u sec.", m_uSleep );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
_LOG( LINF, "[SELECT ] start with host[%s], svc[%s]", szRebalanceHost.c_str(), szService.c_str() );
|
||||
|
||||
// 최종 선택된 FHS 를 작업 처리 list 에 등록처리한다
|
||||
InsertCompleteFhs( szRebalanceHost );
|
||||
|
||||
// 최종 선정된 FHS, 서비스에 대해 MOVE 처리할 content 정보를 조회하여...
|
||||
// Move 처리 대상 content 정보를 저장하는 queue 에 push_back 시킨다.
|
||||
SelectContent( rcdb, szRebalanceHost, szService );
|
||||
|
||||
// RCDB 와의 연결을 명시적으로 해제 처리
|
||||
rcdb.PgCloseDB();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool CTargetSelectThread::CalcInternalLimit( void )
|
||||
{
|
||||
// FHS 의 재선택 방지 관련 time limit 값을 sec 단위로 계산한다.
|
||||
// 300 ~ 86400
|
||||
m_uTimeoutFhsReselection = (CProcessConfig::GetInstance()->GetFhsReselectionTimeLimit()) * 60;
|
||||
|
||||
// rebalance 대상 장비에서 1회 Move 처리시킬 Content 의 최대 크기 계산
|
||||
// 1. 각 copy thread 당 100Mbps 전송으로 계산 : 100Mbps -> 12 Mbyte / sec -> 12000000ULL
|
||||
// 2. MAX_COPY_THREAD_COUNT 정의된 Copy thread count
|
||||
// 3. 전송 시간
|
||||
// - 일반 상황 : FHS 재선택 방지시간
|
||||
// - full hash : FHS 재선택 방지시간 / 2
|
||||
// 4. 안전계수
|
||||
// - 0.7
|
||||
|
||||
// full hash check 선택된 경우
|
||||
if( CProcessConfig::GetInstance()->GetHashCheckLevel() == 2 )
|
||||
{
|
||||
// Copy Thread 가 1개인 경우, N 개인 경우 본 값에 곱하면 됨.
|
||||
// - 재선택 방지 시간이 30 min = 7559999999 = 7.5 GB
|
||||
// - 재선택 방지 시간이 20 min = 5040000000 = 5.0 GB
|
||||
// - 재선택 방지 시간이 60 min = 15119999999 = 15.1 GB
|
||||
// - 재선택 방지 시간이 1 day = 362880000000 = 362 GB
|
||||
|
||||
m_uMaxTransferlimit = (unsigned long long)(12000000ULL * MAX_COPY_THREAD_COUNT * m_uTimeoutFhsReselection / 2 * 7 / 10);
|
||||
}
|
||||
else // full hash 가 아닌.. No check 또는 앞뒤 1MByte check 인 경우.
|
||||
{
|
||||
// Copy Thread 가 1개인 경우, N 개인 경우 본 값에 곱하면 됨.
|
||||
// - 재선택 방지 시간이 30 min = 15119999999 = 15 GB
|
||||
// - 재선택 방지 시간이 20 min = 10080000000 = 10 GB
|
||||
// - 재선택 방지 시간이 60 min = 30239999999 = 30 GB
|
||||
// - 재선택 방지 시간이 1 day = 725760000000 = 725 GB
|
||||
|
||||
m_uMaxTransferlimit = (unsigned long long)(12000000ULL * MAX_COPY_THREAD_COUNT * m_uTimeoutFhsReselection * 7 / 10);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CTargetSelectThread::UpdateCompleteFhs( )
|
||||
{
|
||||
time_t timeNow;
|
||||
time( &timeNow );
|
||||
|
||||
std::list<CCompleteFhsInfo>::iterator it;
|
||||
|
||||
// list 의 erase() 함수 사용시 문제를 해결하기 위해 아래와 같이 처리함.
|
||||
it = m_listCompleteFhs.begin();
|
||||
while( it != m_listCompleteFhs.end() )
|
||||
{
|
||||
if( (timeNow - it->m_timeSelect) > m_uTimeoutFhsReselection )
|
||||
{
|
||||
// expire 시간을 넘긴 경우.. 해당 FHS 삭제 처리
|
||||
_LOG( LINF, "[SELECT ] fhs %s re-select limit expire.", it->m_szHostName.c_str() );
|
||||
|
||||
// list 삭제시 다음 원소 위치 접근 실패 오류를 해결하기 위해 반환값을 it 에 설정 처리.
|
||||
it = m_listCompleteFhs.erase( it);
|
||||
}
|
||||
else
|
||||
{
|
||||
// expire 시간을 넘기지 않은 경우...
|
||||
// queue 개념의 list 라서 다음 FHS 도 expire 시간이 되지 않았으므로... 더이상 check 하지 않는다.
|
||||
// it++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void CTargetSelectThread::InsertCompleteFhs( std::string & szHost )
|
||||
{
|
||||
CCompleteFhsInfo fhs;
|
||||
|
||||
fhs.m_szHostName = szHost;
|
||||
// 시간은 CCompleteFhsInfo 객체 생성시 자동으로 생성됨
|
||||
|
||||
// list push.
|
||||
m_listCompleteFhs.push_back( fhs );
|
||||
|
||||
_LOG( LINF, "[SELECT ] fhs re-select limit list push. [%s current:%ld expire:+%u sec]"
|
||||
, fhs.m_szHostName.c_str(), fhs.m_timeSelect, m_uTimeoutFhsReselection );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool CTargetSelectThread::SelectService( DataBase & rcdb, std::string & szService )
|
||||
{
|
||||
// MOVE 처리를 수행할 서비스를 RCDB 에서 선택한다.
|
||||
|
||||
// RCDB 연결은 이미 되어 있는 상태
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// 1. 서비스 실제 사용량이 1TB 이상인 서비스를 선택
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT sp_svc_tran_id FROM t_dav_use_dir WHERE depth=0 AND dir_used >= 1000000000000; "
|
||||
);
|
||||
|
||||
rcdb.PgDoExec( szQuery );
|
||||
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] service select exec error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = rcdb.GetNoTuples();
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] service select tuple error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 적합한 서비스가 없는 경우..
|
||||
LOG( LWAR, "[SELECT ] service select not found. [%d][%s]"
|
||||
, nResult, szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 1 )
|
||||
{
|
||||
// 조회 결과가 1개인 경우...
|
||||
szService = rcdb.GetValue( 0, 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// N 개의 조회 결과가 존재하는 경우
|
||||
// random 처리하여 1개를 선택한다.
|
||||
|
||||
// random 선택
|
||||
unsigned int uRandom = 0;
|
||||
int fd = open( "/dev/urandom", O_RDONLY );
|
||||
read( fd, &uRandom, sizeof( uRandom ) );
|
||||
close( fd );
|
||||
|
||||
szService = rcdb.GetValue( uRandom % nResult, 0 );
|
||||
}
|
||||
|
||||
rcdb.PgClear();
|
||||
|
||||
// 적합한 서비스 정보 결과 로깅
|
||||
_LOG( LINF, "[SELECT ] service select ok. selected svc_seq [%s]. total svc count [%d]"
|
||||
, szService.c_str() , nResult );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTargetSelectThread::SelectContent( DataBase & rcdb, std::string & szRebalanceHost, std::string & szService )
|
||||
{
|
||||
// RCDB 에서 해당 host, service 에 해당하는 content 를 조회하여 멤버변수인 queue 에 push 처리한다.
|
||||
|
||||
|
||||
// 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 );
|
||||
|
||||
// RCDB 연결은 이미 되어 있는 상태
|
||||
char szQuery[4096];
|
||||
int nResult = 0;
|
||||
|
||||
// 대상 content 조회
|
||||
// - 삭제되지 않았고...
|
||||
// - 원본, 복제본 만.. cache 는 포함하지 않는다.
|
||||
// - 0 파일 제외하고 최소 3 MB 이상 파일
|
||||
// - 최종 수정일이 15일 이상된 content
|
||||
// - 가장 용량이 큰 순서부터
|
||||
// 1. 최소한의 이동으로 FHS 간 균형을 빨리 맞추기 위함
|
||||
// 2. 작은 파일 많이 이동시키면.. RCDB blot 되므로... 용량 큰 파일이 유리
|
||||
// 3. 오래된 파일부터 이동시키면... 실패시에도 계속 오래된 파일부터 시도하므로...
|
||||
// 4. 용량은 m_uMaxTransferlimit 값 이상은 옮길 수 없으므로.. 해당 크기 이하로 설정한다.
|
||||
|
||||
// 기능 수정
|
||||
// - 제한크기 값에 해당하는 content 1개만 선택되었지만.. 해당 content 가 이미 copy 대상 FHS 에 존재하는 경우
|
||||
// - 해당 FHS 는 매번 조회해도 해당 content 만 나와.. 실패처리되어.. rebalance 안되는 상황 발생
|
||||
// - 이를 해결하고자.... 조회시 크기 제한 값을 1GB ~ 전송 제한값 수준까지 GB 단위로 random으로 계산하여 조회되도록 하여
|
||||
// - 문제 content 조회 되지 않을 가능성이 존재하도록 처리.
|
||||
|
||||
// 최대 전송 제한값에서 GB 단위 값을 추출
|
||||
// - 1GB 이하이면 1 로 처리.
|
||||
unsigned long long uSelectSizeLimit = m_uMaxTransferlimit / 1000000000ULL;
|
||||
if( uSelectSizeLimit == 0 )
|
||||
uSelectSizeLimit = 1;
|
||||
|
||||
// nano 시간값을 1~ 전송제한크기 GB 단위로 처리하여 GB 단위 random 값을 얻은 후
|
||||
// 실제 전송 제한값에서 해당 크기만큼 빼서
|
||||
// 조회시 사용할 GB 단위의 크기값을 생성.
|
||||
uSelectSizeLimit = m_uMaxTransferlimit - ((timeStart.tv_nsec % uSelectSizeLimit) * 1000000000ULL);
|
||||
_LOG( LINF, "[SELECT ] select content size limit. [%llu GB]", uSelectSizeLimit/1000/1000/1000 );
|
||||
|
||||
|
||||
// 2021-01-05 NEW huibong 이동 대상 content 선택 관련 mode 기능 추가 (#33306)
|
||||
// - PC-71 에서 조회 후 선택된 content 가 이미 대상 장비에 복제본 등이 존재하여 이동이 실패할 경우....
|
||||
// - 다음 조회시에도 계속 같은 content 가 다시 선택되는 문제가 발생함.
|
||||
// - 따라서 조회시 order by 를 content length 가 아닌 random 으로 처리하는 기능 추가
|
||||
|
||||
// select mode 사용 전 기존 mode 를 timeout 처리해야 하는지 확인 처리
|
||||
CProcessConfig::GetInstance()->SetContentSelectModeToDefault();
|
||||
|
||||
if( CProcessConfig::GetInstance()->GetContentSelectMode() == SELECT_MODE_RANDOM )
|
||||
{
|
||||
_LOG( LINF, "[SELECT ] content select mode : RANDOM " );
|
||||
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT resource_id, uri, sp_svc_tran_id, host_name, filename_hash, get_content_length "
|
||||
"FROM t_meta_%s "
|
||||
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' "
|
||||
"AND host_name = '%s' "
|
||||
"AND get_content_length >= 3000000 AND get_content_length < %llu "
|
||||
"AND get_lastmodified < (SELECT date_part( 'epoch', CURRENT_TIMESTAMP )::int - 1296000 || '000000')::bigint "
|
||||
"ORDER BY random() LIMIT %d;"
|
||||
, szService.c_str()
|
||||
, szRebalanceHost.c_str()
|
||||
, uSelectSizeLimit
|
||||
, CONTENT_SELECT_MAX_LIMIT_COUNT
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default 상황인 경우.. 큰 파일을 우선으로 이동시킨다.
|
||||
snprintf( szQuery, 4096 - 1
|
||||
, "SELECT resource_id, uri, sp_svc_tran_id, host_name, filename_hash, get_content_length "
|
||||
"FROM t_meta_%s "
|
||||
"WHERE resource_type = 0 AND deleted_yn = 'N' AND is_cache != 'Y' "
|
||||
"AND host_name = '%s' "
|
||||
"AND get_content_length >= 3000000 AND get_content_length < %llu "
|
||||
"AND get_lastmodified < (SELECT date_part( 'epoch', CURRENT_TIMESTAMP )::int - 1296000 || '000000')::bigint "
|
||||
"ORDER BY get_content_length DESC LIMIT %d;"
|
||||
, szService.c_str()
|
||||
, szRebalanceHost.c_str()
|
||||
, uSelectSizeLimit
|
||||
, CONTENT_SELECT_MAX_LIMIT_COUNT
|
||||
);
|
||||
}
|
||||
|
||||
// Query 실행
|
||||
rcdb.PgDoExec( szQuery );
|
||||
nResult = rcdb.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, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int uPushTotalCount = 0;
|
||||
|
||||
// 결과 Row 수 확인
|
||||
nResult = rcdb.GetNoTuples();
|
||||
|
||||
_LOG( LDBG, "[SELECT ] content selected. [%s svc:%s select_count:%d ]"
|
||||
, szRebalanceHost.c_str(), szService.c_str(), nResult );
|
||||
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// query 수행 실패시
|
||||
LOG( LERR, "[SELECT ] content select tuple error. [%d][%s][%s]"
|
||||
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else if( nResult == 0 )
|
||||
{
|
||||
// 적합한 Content 가 없는 경우..
|
||||
LOG( LWAR, "[SELECT ] content select not found. [%d][svc:%s][%s][%s]"
|
||||
, nResult, szService.c_str(), szRebalanceHost.c_str(), szQuery );
|
||||
|
||||
// 다음 Query 수행을 위해 조회 결과 set 을 clear 처리
|
||||
rcdb.PgClear();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// N 개의 조회 결과가 존재하는 경우
|
||||
// - 정해진 용량까지 Queue 에 push 처리한다.
|
||||
CRebalanceData data;
|
||||
unsigned long long uPushSum = 0;
|
||||
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
// 0: resouce_id
|
||||
// 1: uri
|
||||
// 2: svc seq
|
||||
// 3: host_name
|
||||
// 4: filename_hash
|
||||
// 5: get_content_legnth
|
||||
data.m_uOriginContentLength = atoll( rcdb.GetValue( i, 5 ) );
|
||||
|
||||
if( uPushSum + data.m_uOriginContentLength > m_uMaxTransferlimit )
|
||||
{
|
||||
// 더 이상 push 처리하지 못하도록 한다.
|
||||
_LOG( LINF, "[SELECT ] content push end by total size %llu GB limit. [%s svc:%s select:%d, push:%u]"
|
||||
, m_uMaxTransferlimit /1000/1000/1000
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, nResult, uPushTotalCount );
|
||||
break;
|
||||
}
|
||||
|
||||
uPushSum += data.m_uOriginContentLength;
|
||||
|
||||
// 확인 결과 resouce_id 의 최대값은 9223372036854775807
|
||||
// long long int 의 최대값은 9223372036854775807 으로 resouce_id 값과 동일
|
||||
// 실제 변환 테스트해 보니 최대값까지 정상 변환 확인함.
|
||||
data.m_uResourceId = atoll( rcdb.GetValue( i, 0 ) );
|
||||
data.m_szURI = rcdb.GetValue( i, 1 );
|
||||
data.m_szServiceSeq = rcdb.GetValue( i, 2 );
|
||||
data.m_szOriginHost = rcdb.GetValue( i, 3 );
|
||||
data.m_szOriginFileNameHash = rcdb.GetValue( i, 4 );
|
||||
|
||||
// push 처리
|
||||
if( m_pQueueMoveStandby->Push( data ) == false )
|
||||
{
|
||||
// push 처리 실패시...
|
||||
// 2 sec 후 재시도해서 실패하면.. queue full 가능성이 있으므로...
|
||||
// 버리고.. loop 중지
|
||||
|
||||
sleep( 2 );
|
||||
if( m_pQueueMoveStandby->Push( data ) == false )
|
||||
{
|
||||
LOG( LWAR, "[SELECT ] content select ok. but queue push fail. [%s] [svc:%s id:%llu size:%llu]"
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// total push count 1 증가
|
||||
uPushTotalCount++;
|
||||
|
||||
// 로깅
|
||||
_LOG( LINF, "[SELECT ] content select, push ok. [%s] [svc:%s id:%llu size:%llu]"
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, data.m_uResourceId, data.m_uOriginContentLength );
|
||||
}
|
||||
}
|
||||
|
||||
rcdb.PgClear();
|
||||
|
||||
// Content 선택 결과 로깅
|
||||
_LOG( LINF, "[SELECT ] content push end. [%s svc:%s select:%d, push:%u]"
|
||||
, szRebalanceHost.c_str(), szService.c_str()
|
||||
, nResult, uPushTotalCount );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/****************************************************************************
|
||||
rc_balanced target content select thread header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/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 __TARGET_SELECT_THREAD_H__
|
||||
#define __TARGET_SELECT_THREAD_H__
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "RcdbInfo.h"
|
||||
#include "Database.h"
|
||||
#include "ShmInfoThread.h"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
|
||||
// 한번 선정되어 rebalance 처리한 FHS 정보 저장을 위한 객체
|
||||
class CCompleteFhsInfo
|
||||
{
|
||||
public:
|
||||
|
||||
// 마지막 선택된 시간
|
||||
time_t m_timeSelect;
|
||||
|
||||
// 장비 name
|
||||
std::string m_szHostName;
|
||||
|
||||
// 생성자 (마지막 선택 시간은 현재 시간으로 설정 처리)
|
||||
inline CCompleteFhsInfo() { time( &m_timeSelect ); }
|
||||
};
|
||||
|
||||
|
||||
// CTargetSelectThread class
|
||||
// - rebalance 처리 대상 FHS 선정 및 Content 선정 처리를 수행
|
||||
// - move 처리할 content 정보를 move standby queue 에 저장 처리
|
||||
// - 한번 선정된 FHS 는 특정 시간동안 재선정 되지 않는다.
|
||||
class CTargetSelectThread
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
CTargetSelectThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
|
||||
, CShmInfoThread * const pShmInfoThread );
|
||||
|
||||
// 소멸자
|
||||
~CTargetSelectThread();
|
||||
|
||||
// Thread 를 생성하여 주기적으로 job 을 처리한다.
|
||||
bool Start( void );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread handle
|
||||
pthread_t m_threadHandle;
|
||||
|
||||
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
|
||||
unsigned int m_uSleep;
|
||||
|
||||
// Move 처리 대상 content 정보를 저장하는 queueMoveStandby 객체에 대한 포인터
|
||||
CDataQueue<CRebalanceData> * m_pQueueMoveStandby;
|
||||
|
||||
// Sharde memory 정보를 추출하여 관리하는 Thread 객체에 대한 포인터
|
||||
CShmInfoThread * m_pShmInfoThread;
|
||||
|
||||
// 선택되어 MOVE 처리한 FHS 는 특정시간동안 다시 선택되지 않도록 목록으로 관리
|
||||
std::list<CCompleteFhsInfo> m_listCompleteFhs;
|
||||
|
||||
// RCDB 연결 정보 관리 객체
|
||||
CRcdbInfo m_rcdbInfo;
|
||||
|
||||
// 선택되어 MOVE 처리한 FHS 의 특정시간 동안 재선택을 방지하기 위한 Timeout 값
|
||||
// 단위: sec
|
||||
// - conf 설정 등을 참고로 초기화시 자동으로 계산됨.
|
||||
unsigned int m_uTimeoutFhsReselection;
|
||||
|
||||
// Rebalance 대상 장비로 선택된 FHS 에서 한번 선택시 전송 처리할 Content 의 최대 크기
|
||||
unsigned long long m_uMaxTransferlimit;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// Thread loop base function
|
||||
static void * threadFunc( void * arg );
|
||||
|
||||
// 실제 Content Copy 처리를 control 하는 함수
|
||||
void Execute( void );
|
||||
|
||||
// FHS 의 재선택 방지 관련 timeout 및 최대 전송 가능 제한값을 계산한다.
|
||||
bool CalcInternalLimit( void );
|
||||
|
||||
// m_queueCompleteFhs 에서 expire 시간이 지난 FHS 정보 삭제 처리.
|
||||
void UpdateCompleteFhs( void );
|
||||
|
||||
// m_queueCompleteFhs 에 FHS 정보 등록
|
||||
void InsertCompleteFhs( std::string & szHost );
|
||||
|
||||
|
||||
// RCDB 에서 Move 처리할 대상 서비스를 선택한다.
|
||||
bool SelectService( DataBase &rcdb, std::string & szService );
|
||||
|
||||
// RCDB 에서 Move 처리할 Content 를 조회하여.. queue 에 push 처리한다.
|
||||
bool SelectContent( DataBase &rcdb, std::string & szRebalanceHost, std::string & szService );
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __TARGET_SELECT_THREAD_H__ */
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
#include "WorkerRebalance.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessRename.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
|
||||
#include "RebalanceData.h"
|
||||
#include "DataQueue.hpp"
|
||||
#include "ProcessMonitoringThread.h"
|
||||
#include "ShmInfoThread.h"
|
||||
#include "TargetSelectThread.h"
|
||||
#include "CopyControlThread.h"
|
||||
#include "RcdbUpdateThread.h"
|
||||
#include "PhysicalDeleteThread.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////
|
||||
|
||||
|
||||
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
|
||||
void ReadyToExitWorkerRebalance()
|
||||
{
|
||||
// Logger 객체 종료 처리.
|
||||
CLogger::Exit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Worker Rebalance 프로세스에 대한 대한 종료 처리 수신시.
|
||||
static void SignalWorkerRebalanceTerminate( int nSignalNumber )
|
||||
{
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
_LOG( LINF, "[Rebalance:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
|
||||
}
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
|
||||
// 종료전 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
// Worker::Rebalance Process 의 signal 처리기...
|
||||
void SetWorkerRebalanceSignalHandler()
|
||||
{
|
||||
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 = SignalWorkerRebalanceTerminate;
|
||||
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
// 나머지 신호는 default 처리.
|
||||
|
||||
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Worker::Rebalance 프로세스의 Main 역활을 수행하기 위한 함수.
|
||||
int WorkerRebalanceMain()
|
||||
{
|
||||
// Process Rename..
|
||||
set_ps_display( PROG_NAME": Rebalance init.", false );
|
||||
|
||||
// Set Signal Handler
|
||||
SetWorkerRebalanceSignalHandler();
|
||||
|
||||
|
||||
// 1. 사용할 객체 초기화 처리
|
||||
|
||||
// Move 처리할 대상 content 를 저장할 Queue 객체
|
||||
CDataQueue<CRebalanceData> queueMoveStandby;
|
||||
queueMoveStandby.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
|
||||
|
||||
// Move 대상 conent 를 다른 FHS 에 Copy 완료한 후 RCDB 업데이트 수행할 content 정보를 저장하기 위한 queue.
|
||||
CDataQueue<CRebalanceData> queueUpdateStandby;
|
||||
queueUpdateStandby.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
|
||||
|
||||
// RCDB UPDATE 처리 후 delete(unlink) 처리 대상 content 를 저장하기 위한 queue
|
||||
CDataQueue<CRebalanceData> queueDeleteStandby;
|
||||
queueDeleteStandby.SetMaxSize( CProcessConfig::GetInstance()->GetMaxQueueSize() );
|
||||
|
||||
// Shared memory 접근 및 주요 정보 추출 관련 thread 객체 생성
|
||||
CShmInfoThread objShmInfoThread;
|
||||
|
||||
// Rebalance 처리 대상 FHS, content 선정을 수행하는 thread 객체 생성
|
||||
CTargetSelectThread objTargetSelectThread( &queueMoveStandby, &objShmInfoThread );
|
||||
|
||||
// Content Copy 처리를 수행할 thread 객체 생성
|
||||
CCopyControlThread objCopyControlThread( &queueMoveStandby, &queueUpdateStandby, &objShmInfoThread );
|
||||
|
||||
// RCDB update 처리를 수행할 thread 객체 생성
|
||||
CRcdbUpdateThread objRcdbUpdateThread( &queueUpdateStandby, &queueDeleteStandby );
|
||||
|
||||
// Process Monitoring thread 객체 생성
|
||||
CProcessMonitoringThread objProcessMonitoringThread( &queueMoveStandby, &queueUpdateStandby, &queueDeleteStandby, &objCopyControlThread );
|
||||
|
||||
// 물리 파일 삭제(unlink) 처리 thread 객체 생성
|
||||
CPhysicalDeleteThread objPhysicalDeleteThread( &queueDeleteStandby, &objProcessMonitoringThread );
|
||||
|
||||
|
||||
|
||||
// 2. 각 모듈 기동 처리
|
||||
// - 최종 작업 모듈부터 기동 처리
|
||||
|
||||
set_ps_display( PROG_NAME": Rebalance start.", false );
|
||||
|
||||
// 공유메모리 접근 및 Rebalance 처리 대상, Copy 수신 대상 FHS 정보 수집을 담당하는 Thread 기동 처리
|
||||
// - 정상 기동시 공유메모리 정보를 scan 해 올 수 있도록 잠시 대기한다.
|
||||
if( objShmInfoThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Shm info thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
else
|
||||
{
|
||||
sleep( 3 ); // 성공시 3 sec 대기
|
||||
}
|
||||
|
||||
|
||||
// 물리 파일 삭제(unlink) 처리를 수행할 thread 기동 처리.
|
||||
if( objPhysicalDeleteThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Physical delete thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// RCDB update 처리를 수행할 thread 기동 처리.
|
||||
if( objRcdbUpdateThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Rcdb update thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// Content copy 처리를 수행할 thread 기동 처리.
|
||||
if( objCopyControlThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Content copy control thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// rebalance 처리 대상 FHS, content 선정을 수행하는 thread 기동 처리
|
||||
if( objTargetSelectThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Target select thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// 프로세스 모니터링 thread 기동 처리.
|
||||
if( objProcessMonitoringThread.Start() == false )
|
||||
{
|
||||
_LOG( LWAR, "[Rebalance:%d] Process monitoring thread start failed. => Process Exit", getpid() );
|
||||
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지...
|
||||
}
|
||||
|
||||
// 대기
|
||||
while( 1 )
|
||||
{
|
||||
// 할일 없어서.. 시그널 수신까지 대기 한다.
|
||||
pause();
|
||||
}
|
||||
|
||||
// 종료시 정리 작업 수행
|
||||
// - 실제로는 동작하지 않는 code
|
||||
ReadyToExitWorkerRebalance();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/****************************************************************************
|
||||
Rebalance worker process main
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/02/25
|
||||
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_REBALANCE_H__
|
||||
#define __WORKER_REBALANCE_H__
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Worker::Rebalance 프로세스의 Main 역활을 수행하기 위한 함수.
|
||||
int WorkerRebalanceMain( void );
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __WORKER_REBALANCE_H__ */
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
// sample 소스 파일
|
||||
|
||||
#include ""
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/****************************************************************************
|
||||
rc_balanced header
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2019/03/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 __REBALANCE_DATA_H__
|
||||
#define __REBALANCE_DATA_H__
|
||||
|
||||
#include <>
|
||||
|
||||
|
||||
|
||||
// class 설명
|
||||
class C
|
||||
{
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __REBALANCE_DATA_H__ */
|
||||
|
||||
Reference in New Issue
Block a user