This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
+42
View File
@@ -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
+37
View File
@@ -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
+67
View File
@@ -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
+304
View File
@@ -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__ */
+595
View File
@@ -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;
}
+119
View File
@@ -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__ */
+215
View File
@@ -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;
}
+114
View File
@@ -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__ */
+110
View File
@@ -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;
}
+82
View File
@@ -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__
+487
View File
@@ -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;
}
+273
View File
@@ -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__
+68
View File
@@ -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
+185
View File
@@ -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;
}
+37
View File
@@ -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__
+152
View File
@@ -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;
}
+62
View File
@@ -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__ */
+222
View File
@@ -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__ */
+127
View File
@@ -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__ */
+45
View File
@@ -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
#-----------------------------------------------#
+43
View File
@@ -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;
}
}
+45
View File
@@ -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__ */
+461
View File
@@ -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;
}
+121
View File
@@ -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__ */
+355
View File
@@ -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)
}
+72
View File
@@ -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__ */
+171
View File
@@ -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__ */
+184
View File
@@ -0,0 +1,184 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/09
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include "Database.h"
#include "Logger.h"
DataBase::~DataBase()
{
if(m_PGconn != NULL) {
PgCloseDB();
}
}
PGconn *DataBase::PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout)
{
ostringstream conninfo;
// CHG 2014-10-29 huibong
// application_name add to logging RCDB
conninfo << "host=" << strHost << " port=" << Port << " dbname=" << strDBName <<
" user=" << strAcct << " password=" << strPasswd << " connect_timeout=" << timeout <<
" application_name=" << PROG_NAME;
#ifdef _USE_LIBPQ_KEEPALIVE
// This option is supported by at least 9.1.2. and Currently supports Linux systems.
conninfo <<" keepalives=1";
#endif // _USE_LIBPQ_KEEPALIVE
//m_PGconn = PQsetdbLogin(strHost.c_str(), szPort, NULL, NULL, strDBName.c_str(), strAcct.c_str(), strPasswd.c_str());
m_PGconn = PQconnectdb(conninfo.str().c_str());
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
PGconn *DataBase::PgOpenDB(const char *pszDBName)
{
m_PGconn = PQsetdb(NULL, NULL, NULL, NULL, pszDBName);
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
void DataBase::PgCloseDB()
{
if(m_PGconn != NULL)
{
PQfinish(m_PGconn);
m_PGconn = NULL;
}
}
int DataBase::PgResult(CFLAG flag)
{
int fRet = 0;
int Result;
Result = PQresultStatus(m_pRes);
switch(Result) {
case PGRES_EMPTY_QUERY :
fRet = -1;
break;
case PGRES_BAD_RESPONSE :
fRet = -2;
break;
case PGRES_NONFATAL_ERROR :
fRet = -3;
break;
case PGRES_FATAL_ERROR :
fRet = -4;
break;
case PGRES_TUPLES_OK :
fRet = 1;
break;
case PGRES_COMMAND_OK :
fRet = 2;
break;
}
if(fRet < 0) {
m_ErrorMessage = PQresultErrorMessage(m_pRes);
}
m_ResultCode = Result;
if(flag == CLEAR && m_pRes) {
PQclear(m_pRes);
m_pRes = NULL;
}
return fRet;
}
string &DataBase::GetErrorMessage()
{
return m_ErrorMessage;
}
int DataBase::GetCmdTuples()
{
//fprintf(stderr, "DataBase::GetCmdTuples PQcmdTuples %s\n", PQcmdTuples(m_pRes));
return (int)atoi(PQcmdTuples(m_pRes));
}
int DataBase::GetNoTuples()
{
return PQntuples(m_pRes);
}
int DataBase::GetNoFields()
{
return PQnfields(m_pRes);
}
PGresult *DataBase::GetRes()
{
return m_pRes;
}
void DataBase::PgClear()
{
if(m_pRes)
{
PQclear(m_pRes);
m_pRes = NULL;
}
}
char *DataBase::GetValue(int tuple, int field)
{
return PQgetvalue(m_pRes, tuple, field);
}
int DataBase::PgDoExec(char *pszQuery)
{
return this->PgDoExec(pszQuery, NOT_CLEAR);
}
int DataBase::PgDoExec(string &strQuery)
{
return this->PgDoExec((char *)strQuery.c_str(), NOT_CLEAR);
}
int DataBase::PgDoExec(char *pszQuery, CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexec(m_PGconn, pszQuery);
int r = PgResult(flag);
return r;
}
int DataBase::PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexecParams(m_PGconn, pszQuery, nParamCnt, NULL, paramValues, NULL, NULL, 0);
int r = PgResult(flag);
return r;
}
int DataBase::PgEscapeString(char *to, const char *from, size_t length)
{
int retval = 0;
//PQescapeStringConn(m_PGconn, to, from, length, &retval);
PQescapeString(to, from, length);
return retval;
}
+56
View File
@@ -0,0 +1,56 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __POSTGRESQL_DATABASE_H__
#define __POSTGRESQL_DATABASE_H__
#include <iostream>
#include <string>
#include "libpq-fe.h"
#define MaxSizeOfDBQuery 1024*9
using namespace std;
class DataBase
{
public:
enum CFLAG { CLEAR = 1, NOT_CLEAR };
DataBase() { m_PGconn = NULL; m_pRes = NULL;}
~DataBase();
PGconn *PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout = 10);
PGconn *PgOpenDB(const char *pszDBName);
void PgCloseDB();
int PgResult(CFLAG flag);
PGconn *GetPgConn(){ return m_PGconn;}
PGresult *GetRes();
void SetRes(PGresult * v) {m_pRes = v;}
int GetCmdTuples();
int GetNoTuples();
int GetNoFields();
int GetResultCode() { return m_ResultCode; }
char *GetValue(int tuple, int field);
void PgClear();
int PgDoExec(string &strQuery);
int PgDoExec(char *pszQuery);
int PgDoExec(char *pszQuery, CFLAG flag);
int PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag = NOT_CLEAR);
int PgEscapeString(char *to, const char *from, size_t length);
string &GetErrorMessage();
private:
int m_ResultCode;
PGconn *m_PGconn;
PGresult *m_pRes;
string m_ErrorMessage;
};
#endif // ~__POSTGRESQL_DATABASE_H__
+464
View File
@@ -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;
}
+62
View File
@@ -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__ */
+87
View File
@@ -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__ */
+361
View File
@@ -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;
}
+159
View File
@@ -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( &currentTm, 0x00, sizeof( struct tm ) );
localtime_r( &currentTime, &currentTm );
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__ */
+40
View File
@@ -0,0 +1,40 @@
#include "RcdbInfo.h"
#include "ProcessConfig.h"
// 생성자..
CRcdbInfo::CRcdbInfo()
{
}
// 소멸자..
CRcdbInfo::~CRcdbInfo()
{
}
// CServiceConfig Class 에 저장된 RCDB 접속 정보를 가져와 멤버 변수에 저장한다.
void CRcdbInfo::Load()
{
// CProcessConfig Class 에서 기본적인 설정값에 대해 검사를 수행하므로...
// 본 Class 에서 다시 유효성 검사를 할 필요는 없다.. ( 어차피 유효성 검사 방법이 동일하므로.. )
// RCDB IP
m_strRcdbIp = CProcessConfig::GetInstance()->GetRcdbIp();
// RCDB Port
m_nRcdbPort = CProcessConfig::GetInstance()->GetRcdbPort();
// RCDB DB Name
m_strRcdbName = CProcessConfig::GetInstance()->GetRcdbName();
// RCDB 접근 계정
m_strRcdbAcct = CProcessConfig::GetInstance()->GetRcdbAcct();
// RCDB 접근 Password.
m_strRcdbAcctPw = CProcessConfig::GetInstance()->GetRcdbAcctPw();
return;
}
+52
View File
@@ -0,0 +1,52 @@
/****************************************************************************
RCDB 접속 정보 저장 처리 Class
-----------------------------------------
begin : 2013/11/13
copyright : (C) 2005 Solbox Inc.
author : Dev Storage Team
email : huibong@solbox.com
version : 3.5
CopyRight(C) 2005 Solbox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of Solbox Inc.
*****************************************************************************/
#ifndef __RCDB_INFO_H__
#define __RCDB_INFO_H__
#include <unistd.h>
#include <string>
/// @brief CRcdbInfo
/// 본 클래스는 conf 파일에 저장된 RCDB 접속 관련 정보를 저장하기 위한 공용 Class...
/// 본 클래스를 사용하지 않고... CServiceConfig class 의 Get 함수를 이용하여 RCDB 접속 정보를 처리해도 된다..
/// 하지만... RCDB 접속 처리 Class 함수의 string type 변수 사용으로 인해... 여러 모듈에서 반복적인 동일 코드 처리 작업을 수행해야 하므로...
/// 본 Class 를 통해 코드를 단순화 시킬 목적으로 사용한다.
class CRcdbInfo
{
public:
// 생성 및 소멸자.
CRcdbInfo();
~CRcdbInfo();
// CServiceConfig class 에 저장된 RCDB 접속 정보를 멤버 변수에 저장처리한다.
void Load( void );
public:
// 멤버 변수들은 DB 접속을 처리하는 객체에서 직접 Access 해서 사용할 수 있도록 public 으로 구성. ( 편의성 제공 목적)
std::string m_strRcdbIp; // RCDB IP
unsigned int m_nRcdbPort; // RCDB Port
std::string m_strRcdbName; // RCDB DB Name
std::string m_strRcdbAcct; // RCDB 접근 계정
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
};
#endif /* __RCDB_INFO_H__ */
+244
View File
@@ -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;
}
+77
View File
@@ -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__ */
+49
View File
@@ -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;
}
+90
View File
@@ -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__ */
+716
View File
@@ -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( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
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( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
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( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
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( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
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( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
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( &currentClock, 0x00, sizeof( struct timespec ) );
clock_gettime( CLOCK_MONOTONIC, &currentClock );
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;
}
}
+100
View File
@@ -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__ */
+630
View File
@@ -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;
}
+126
View File
@@ -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__ */
+222
View File
@@ -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;
}
+37
View File
@@ -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__ */
+6
View File
@@ -0,0 +1,6 @@
// sample 소스 파일
#include ""
+32
View File
@@ -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__ */