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
+698
View File
@@ -0,0 +1,698 @@
Revision 1428
-------------------
수정일 : 2017-11-15
수정자 : 유희곤
- CHG: replication 실패 관련 report 로깅 항목 조정(#31526)
- replication thread 에서 각 FHS 의 작업 결과 수신후
- error message 가 "source file size not match" 로 시작되는 경우 report 로깅 하지 않도록 처리
- 나머지 오류는 "not found" 와 같이 다양, 중요한 로그이므로 report 로그에 기록되도록 함.
- 수정 파일: ReplicateThread.cpp
Revision 1233
-------------------
수정일 : 2015-09-14
수정자 : 유희곤
- CHG: 최신 BaseSocket 모듈 적용 (#24369, #24141)
- EPIPE 및 usleep 관련 수정 버전 적용
- 수정 파일: lib/BaseSocket.cpp
- CHG: usleep -> nanosleep 변경 (#24369)
- multi thread 에서 lock 관련 문제점 발생 가능.
- 프로세스 기동 및 시그널 처리에서만 사용하여... thread 와 연관 없지만.. 수정 처리함.
- 수정 파일: Main.cpp, Worker.cpp
- CHG: Main 버전 (3.5.0) 변경 사항 적용
- 기존 3.4.0 -> 3.5.0 으로 버전 변경 처리.
Revision 1190
-------------------
수정일 : 2015-03-26
수정자 : 유희곤
- BUG: Cache 가 먼저 생성된 경우 replication 생성 관련 문제점 수정 (#21903)
- cache 가 먼저 생성된 경우 복제 대상 선택시 cache 까지 선택되는 문제점 수정
- 수정파일: ReplicateSelect.cpp
Revision 1182
-------------------
수정일 : 2015-02-12
수정자 : 유희곤
- CHG: 신규 RCDB 형상 적용에 따른 Replication, Cache 완료 Insert Flow 최적화 (#21354)
- 기존 pgpool 형상에서 사용 불가했던 INSERT-SELECT query 를
- streaming replication 구조의 신규 RCDB 형상에서 사용 가능해 짐.
- 이에 따라 Replication, Cache 생성 완료 처리 Flow 를 신규 RCDB 형상에 맞도록 최적화하여
- RCDB 에 대한 Query 수행 횟수를 줄인다.
- 수정파일: ReplicateInsert.cpp CacheInsert.cpp
- CHG: glibc 버그 fix 장비에서 binary 생성.
- 세부 내역은 CVE-2015-0235 (gethostbyname_r 버그) 참고
Revision 1125
-------------------
수정일 : 2014-11-17
수정자 : 유희곤
- CHG: DNS resolve 실패시 Retry 기능 추가 (#20812, #20783)
- 통신 접속 관련 lib/CBaseSocket 모듈에서
- getaddrinfo() 의 DNS resolve 실패가 자주 발생함.
- 오류코드 FreeBSD : EAI_NONAME(8), Linux: EAI_AGAIN(-3)
- 이에 3회 재시도 기능 추가
- 수정파일: lib/BaseSocket.cpp
Revision 1102
-------------------
수정일 : 2014-10-29
수정자 : 유희곤
- NEW: RCDB 로그, 모니터링 상에 application_name 정보 표시되도록 기능 추가
- RCDB 연결 관련 PgOpenDB() 함수 호출시
- " application_name=" << PROG_NAME; 을 추가하여
- RCDB 로그, 모니터링 정보 상에 정보가 표시되도록 한다.
- 수정: Database.cpp
Revision 1077
-------------------
수정일 : 2014-09-02
수정자 : 유희곤
- CHG: SKIP_KEYWORD conf section 변경 지원 (#20201)
- program section 을 우선적으로 사용
- 존재하지 않을 경우 common section 을 사용토록 수정 처리
- CHG: 기타
- 소스 상의 잘못된 공백 관련 제거 처리
Revision 1017
-------------------
수정일 : 2014-07-24
수정자 : 유희곤
상세 수정사항은 일감 #19042 을 참고할 것.
- CHG: 양방향 Main 버전 (3.4.0) 변경 사항 적용
- 기존 3.3.0 -> 3.4.0 으로 버전 변경 처리.
- CentOS 5.4 및 6.4 에서 컴파일 가능토록 hearder include 조정 처리
- CHG: 기본 프레임 구조 변경
- 기존 Guard - Worker 구조에서 Main - Worker 구조로 변경 처리
- daemon 함수 사용으로 변경.
- 각 시그널 처리 및 프로세스 기동관련 처리 함수 검증 및 변경.
- BaseSocket 관련 최신 모듈 적용.
- CHG: Conf 처리 관련 신규 singleton 객체 추가
- 기존 전역 구조체 처리 방식에서 sigleton 객체로 처리방식 변경
- conf 변경 항목 내역 반영
- CHG: Log 처리 객체 변경
- 팀 공통 규칙은 singleton 방식의 CLogger 객체 사용으로 방식 변경.
- Report 로그 처리 관련 전용 객체 사용으로 방식 변경 처리
- CHG: 신규 RCDB 형상 지원
- 기존 RCDB t_dav_resource 사용 모듈에 대해
- 해당 Table 존재여부 검사 후... 구형 RCDB, 신형 RCDB(서비스별 Table 분리) 지원토록 기능 수정
- CHG: ftsd 와 통신 관련
- 최신 라이브러리 적용.
- BUG: database class 사용 관련 잘못된 함수 호출 문제
- 이미 CLEAR 처리된 result set 에 대해 tuple 계산함수 호출 등의 버그 수정 처리
- 최신 database class 사용 처리.
- BUG: CProperFhsUpdater 모듈
- vector 의 at() 함수 관련 방어 코드 추가.
- 중복된 FHS 선택 및 무한 루프 발생 가능 코드 수정
- RCDB 연결에 따른 목록 관리 업무 flow 수정
- 비효율적 업무 Flow 코드 수정
- BUG: CQueue Template 을 CDataQueue Template 모듈로 재개발
- Queue 처리 관련 각종 버그 수정
- 잘못된 업무 Flow 수정 처리.
- 모듈 독립성을 위해 .hpp 로 재개발 수행
- BUG: ProcessStatus 모듈 재개발
- 잘못된 Thread Count 관리 코드가 존재. 잘못된 업무 Flow 코드 존재
- 이로 인해 전부 재개발 처리.
- Thread 기능 탑재하여 2 sec 마다 Process 표시 상태를 자동 갱신토록 기능 추가
- CHG: Replication 관련
- Replication 대상 Content 조회 Query 상의 GROUP BY 구문으로 인해 불필요한 DISTINCT 구문 제거
- ftsd 로 부터 전달받는 응답에 대해 잘못된 실패 count 처리 관련 업무 Flow 버그 수정
- CHG: CReplicateSelect 모듈 수정
- loop 간 sleep time 을 60, 15, 2 sec 에서 60, 30 sec 로 업무 flow 단순화 처리
- 시간 동기화 관련 문제 발생시에 대한 방어 기능 추가
- 불필요, 잘못된 코드 전부 제거
- RCDB 오류 처리 관련 기능 추가
- 잘못된 db handler 관련 코드 수정.
- BUG: CReplicateInsert 모듈 수정
- 잘못된 pop 및 대기 처리 로직 수정
- 상황에 따른 sleep time 조정 처리
- 불필요한 대기 코드 전부 제거
- RCDB 오류 처리 관련 기능 추가
- 잘못된 db handler 관련 코드 수정.
- BUG: 재시도 관련 MAX_RETRY_COUNT 값에 대한 비교 코드 통일
- 모듈별 잘못된 해석으로 인해 재시도 여부 판단이 > , >= 으로 나뉘어져...
- 잘못된 업무 Flow 발생 가능.
- 이에 MAX_RETRY_COUNT 값을 초과하는 경우에만 Throw 되도록 관련 코드 통일
- CHG: Conf 항목 수정
- FHS_STATUS_BROADCAST_UDP_PORT 항목 미사용으로 인한 삭제 처리
- Hot Content 의 URI 정보 전송으로 인한 FHS_STORAGE_MOUNT 항목 미사용. 삭제 처리
- fimngd 로 부터 Hot content 정보 수신을 기존 UDP 13110 Broadcasting 에서 TCP 13110 port Listen 으로 변경
이에 conf 상의 FHS_HOTCONTENTS_CONTROL_UDP_PORT = 13110 설정을
FHS_HOTCONTENT_LISTEN_TCP_PORT = 13110 로 변경
- Hot Content 의 Cache 생성 조건 검사를 위한 동접 fd 최소값 항목 추가
HOTCONTENT_CREATE_MINIMUM_FD_COUNT = 5
- NEW: fimngd 와의 통신 방식 변경
- 변경된 conf 적용 관련 기능 추가 및 수정
- fimngd 와의 통신 처리를 담당할 CFimngdSocketControl 모듈 추가
- 변경된 Protocol 관련 CHotContentInfo 모듈 추가 ( 기존 모듈 재개발 )
- Worker 상에 통신 대기 및 수신 Thread 관련 기능 추가
- fimngd 로 부터 수신되는 content length 정보는 물리 파일 실 size 정보임.
- CHG: Cache Job 관련 모듈 재개발
- 관련 Data 객체가 5개씩이나 되어 업무 Flow 파악 어려움
- 관련 Data 객체 통합하여 재설계
- Job 관리 모듈 재개발 및 기능 추가
- NEW: 신규 RCDB 형상 지원 관련 미사용 column 제거
- #19575 을 통해 신규 RCDB 지원시 제거된 column 항목 반영 처리.
- 제거항목: get_content_language, get_etag, lock_discovery, supported_lock
- CReplicateInsert , CCacheInsert 모듈 수정
- CHG: Cache Order 관련 모듈 재개발
- RCDB 접속 최소화 처리 기능 추가.
- 변경된 Data 객체 반영 처리
- 신규 RCDB 형상 지원 관련 코드 추가
- Cache 생성시 항상 원본 Content 사용 -> 원본, 복제본, cache 생성본 사용가능토록 수정
- CHG: 기타
- Class 및 변수명 단순화 ( 이로 인해 파일명 변경 발생 )
- Replication, Cache 관련 작업 Thread 기동 실패시 메모리 leak 발생 방어코드 추가
Revision 935
-------------------
수정일 : 2014-01-20
수정자 : 김오종
- DEL: PROPER_FHS_UPDATE 항목을 제거한다.
- DB로 부터 Proper fhs 값을 주기적으로 가져오는 것은 더이상 옵션화 대상이 아니라 구동 될때 마다 가져오는것이다.
Revision 930
-------------------
수정일 : 2014-01-14
수정자 : 김오종
- CHG: rc_rmcd의 프로세스 형식에서 guard의 개념이 없어서 소스의 파일명을 변경한다.
- Guard.cpp Guard.h 를 main.cpp main.h로 변경
- DEL: rc_statd의 신규제작에 따른 기능 축소 및 변경
- top fhs를 선정하기 위한 기능을 rc_statd가 하게됨에 따라 vmasd 수신기능을 제거한다.
- Internal Cache 생성시 target fhs 선정에 대한 부분을 RCDB의 t_fhs_top_satus table을 참조하여 선정하도록 수정
- CHG: report로그의 포맷을 변경처리 한다.
Revision 0822
-------------------
수정일 : 2012-09-25
수정자 : 유희곤,노경민
- NEW: 특정 단어가 포함된 경우 복제 대상에서 제외 기능추가 (Keyword Skip)
- 복제 대상 URI에 특정 단어가 포함된 경우 복제에서 제외되는 기능을 추가한다.
- 해당 체크 시 대소문자에 대해서 양방향 기본 정책을 따른다.(대소문자 구분)
- 해당 config 설정은 SKIP_KEYWORD 세션에 추가되며, 구분자는 ,이다.
- 해당 cofnig 설정 형식은 check type | keyworkd 이며,
check type은 h(head) - path 시작, m(mddle) - path 가운데 , t(tail) - path 끝으로 구분 된다.
- 수정된 파일은 define_rc_rmcd.h, GetConfig.cpp, ReplicateTargetSelector.cpp, ReplicateTargetSelector.h 임
- DEL: 코어 덤프 생성 로직 삭제
- 코어 덤프 발생 시 특정 디렉토리에 발생되도록 생성된 로직을 삭제처리함
- 수정된 파일은 Worker.cpp, Guard.cpp임
- NEW: Replicaiton 정보 RCDB INSERT 후 재확인 기능 추가 (정합성 체크)
- Replication 완료된 Content 정보를 RCDB 에서 SELECT -> INSERT 처리시 원본 변경되어 하여 서비스 장애발생 가능
- 이를 보완하기 위해 RCDB INSERT 후 다시 한번 원본의 변경 여부를 검사하여...
- 원본이 변경된 경우 INSERT 된 Replication 정보를 deleted_yn = 'Y' 로 변경 처리한다.
- 수정 소스 : CompleteTargetInserter.cpp 의 InsertFromQueue()
- CHG: 잘못된 로그 메시지 수정
- CacheDeleteCheck.cpp 의 CheckTimeoutCacheContens() 상의 잘못된 로그 수정 처리.
- DEL: Makefile install 항목에서 conf 파일 copy 처리 기능 제거
- -cp -n ../$(PROG_NAME).conf $(INSTALL_CONF)/$(PROG_NAME).conf
- Linux 에서 -n 옵션 미지원 및 패키징 처리로 인해 해당 기능 제거 및 관련 항목 제거 처리
- CHG: 양방향 Main 버전 (3.2.0) 변경 사항 적용
- 기존 3.1.0 -> 3.2.0 으로 버전 변경 처리.
Revision 0723
-------------------
수정일 : 2012-02-29
수정자 : 노경민
- CHG: Postgresql Client Library 변경
- 8.4.5 -> 9.1.3 으로 변경
- HA로 구성된 DBLB 사용 시 HA전환 발생 시 쿼리 실행 중인 경우 block 되는
현상을 방지하기 위해 TCP keepalive 기능을 사용하여야 한다. linux 경우 각
실행 데몬에서 해당 기능을 소켓 옵션 함수 사용하여 on 시켜야 되며,
Postgresql 경우 기존 8.4.5 버전에 포함된 client library 경우 해당 기능을
지원하지 않으며, 9.1.x 대에 해당 기능을 지원 확인되어 관련 library 재컴파일함
Revision 0685
-------------------
수정일 : 2012-02-22
수정자 : 노경민
- CHG: 버전 변경
- CentOS용 지원 버전인 3.1.0으로 변경함
- BUG: 복사된 파일 경우 Internal Cache가 발생하지 않는 현상 발견
- Revision r337 수정된 문제는 opendav 관련 문제였으며, 관련 opendav 패치완료
- filename_hash 중복 체크 로직 삭제 처리함
- 수정된 파일은 CacheCompleteInserter.cpp임
- CHG: conf 통합에 따른 변경
- FHS_VMASD_RCID -> RC_ID
- FHS_VMASD_POR -> FHS_STATUS_BROADCAST_UDP_POR
- FHS_CMA_PORT -> FHS_HOTCONTENTS_CONTROL_UDP_PORT
- 수정된 파일은 Guard.cpp, GetConfig.cpp, define_rc_rmcd.h, Worker.cpp임
- CHG: 복제 지연 발생 로직 변경
- work thread가 10개이상 발생하지 부분 수정
- 복제 완료 관련 큐가 비워있을 때 과도하게 sleep된 부분 수정
- 수정된 파일은 CacheControl.cpp, CacheCompleteInserter.cpp, ReplicateControl.cpp임
- CHG: OS 변경 관련 기능
- CentOS 5.4 정상 동작 확인
- Process Title 관련 문제 해결(setproctitle)
- Postgresql 소스 활용(src/backend/utils/misc/ps_status.c)
- 수정된 파일은 Util.cpp(h), Guard.cpp, Worker.cpp, ProcessStatus.cpp임
- CHG: PGPool 관련 Mismatch 문제 해결
- INSERT~SELECT를 사용해서 발생한 문제임
- 수정된 파일은 CompleteTargetInserter.cpp,CacheCompleteInserter.cpp임
- CHG: 복제 완료 시 Insert 오류 시 무한 반복 해결
- 오류 발생 시 관련 큐를 삭제해주지 않아서 발생한 문제임
- 수정된 파일은 CompleteTargetInserter.cpp임
Revision 0620
-------------------
수정일 : 2011-09-21
수정자 : 김오종
- BUG: 캐쉬 파일 삭제 시 get_lastmodified 업데이트 하지 않음.
- CQueuePopControl::GetWork() 함수내에서 get_lastmodified 업데이트 하도록 수정.
Revision 0604
-------------------
수정일 : 2011-09-02
수정자 : 김오종
- BUG: Proper FHS 대상 장비가 없는 경우 report.log 파일에 로깅이 안됨.
- CQueuePopControl::GetWork() 함수 수정.
- 재시도 회수를 확인하고 넘었다면 pop해서 버린다.
- report.log에 logging되도록 수정.
Revision 0571
-------------------
수정일 : 2011-06-08
수정자 : 김오종
- BUG: 복제 본 재시도 처리 과정에서 Ignorelist 처리 버그 수정.
- CReplicateControl::Execute() 함수 수정.
- 복제 본 요청 후 Rep Count 만큼 처리 되지 않은 경우 재처리 하게 된다.
- 그 과정에서 성공한 fhs의 목록은 복제처리대상 목록에서 제외 시키는 과정의
로직에서 정상적으로 처리 되지 않는 부분을 수정.
- 본 문제는 STL list의 '=' operator 만 사용하도록 함으로써 수정 함.
Revision 0564
-------------------
수정일 : 2011-06-07
수정자 : 김오종
- BUG: vmasd 수신 모듈 중 CFhsInfo::m_nManualDisable 에 처리되지 않았던 fsck 상태에대한 처리.
- CProperFhs::DoProperFhs() 함수내에 수정.
- CHG: Process 시작 시점에 각 object 들의 생성 여부를 logging 처리함.
- main.cpp 의 WorkerMain( ) 함수 내에 log 정보 추가.
Revision 0529
-------------------
수정일 : 2011-05-03
수정자 : 유희곤
- BUG: lib/BaseSocket.cpp ConversionAddr() 함수 수정
- ConversionAddr() 함수에서 gethostbyname() 함수 사용으로 인한 Thread-Safe 문제 발생
- Multi Thread App에서 요청한 장비와 다른 장비로 접속하는 문제가 발견되었으며
- 이로인해 Contents 전송, 관리 부분에서 문제가 발생할 수 있어 수정 처리함.
- 본 문제는 Thread Safe 한 getaddrinfo() 함수 사용으로 변경 처리함.
Revision 0513
-------------------
수정일 : 2011-04-07
수정자 : 노경민
- BUG: 복제 되지 않는 파일에 대한 복사된 파일에 대한 복제되지 않는 문제
- 복제 완료 후 DB insert 시 filename_hash 중복 체크 로직 때문에 발생함
- 복제 완료 전 복사된 파일에 대한 복제본 허용함
- 수정된 파일은 CompleteTargetInserter.cpp, CompleteTargetInserter.h, Data.h, ReplicateTargetSelector.cpp 임
- CHG: config 통합
- rcts.conf로 통합함.
- 기존 rc_rcrmc.conf 삭제함
- 수정된 파일은 GetConfig.cpp, define_rc_rmcd.h, Makefile 임
- CHG: 최초 기동시 Replication Select Interval max 설정 기준 삭제
- 72시간 이상 설정되지 않던 부분을 수정함
- 수정된 파일은 ReplicateTargetSelector.cpp 임
Revision 0492
-------------------
수정일 : 2011-03-09
수정자 : 김오종
- BUG: replication 완료 후 인서트 처리하는 시에 filename_hash의 중복이 있는 상황
위 상황에서 complete queue에서 pop()해서 제거하지 않는 버그 수정.
- filename_hash 중복이 있을 경우 complete queue에서 pop()하도록 한다.
- pop()된 내용을 report.log에 기록한다.
- CompleteTargetInserter.h 소스코드 수정
- CompleteTargetInserter.cpp 소스코드 수정
Revision 0467
-------------------
수정일 : 2010-12-29
수정자 : 장희준
- CHG: 캐시 요청 처리 시 로깅 부분 보완.
- CmaReceiver.cpp 코드 수정
Revision 0466
-------------------
수정일 : 2010-12-29
수정자 : 장희준
- CHG: Revision 0465에서 로깅 부분 보완.
- CmaReceiver.cpp 코드 수정
Revision 0465
-------------------
수정일 : 2010-12-29
수정자 : 장희준
- CHG: 캐시 요청에 대한 RCID 판변 추가.
- CmaReceiver.cpp 코드 수정
Revision 0457
-------------------
수정일 : 2010-12-13
수정자 : 장희준
- CHG: Internal Cache 생성 관련 상세 로그 추가
- CCacheOrder::RequestJob() 함수에서
- nTuple == 0 이면 WARN_LOG( "GetNoTuples has no result. sql: %s", szQuery );
- nTuple < 0 이면 ERROR_LOG("GetNoTuples has failed. : errmsg:%s", m_pPgSQL->GetErrorMessage().c_str());
- CHG: DB 접속 불가시 처리 로직 추가
- CProperFhsUpdater::EntryPoint 함수에서
- pObject->Execute() == false면 10초 후에 Execute()를 호출하도록 변경.
Revision 0456
-------------------
수정일 : 2010-12-10
수정자 : 장희준
- BUG: 최초 기동시 Replication Select Interval 계산식에서 변수 범위 관련 버그 수정
- CReplicateTargetSelector::SetTargetSelectInterval() 함수상에서
- m_nTargetSelectInterval = (long long) ( sec * 1000000 ); 부분에서 (long long) (int) 형상으로 계산되므로
- Replication Select Interval 에 대한 정확한 계산이 수행되지 않음.
- 이에 따른 수정처리 진행함.
Revision 0454
-------------------
수정일 : 2010-12-09
수정자 : 유희곤
- BUG: 최초 기동시 Replication Start Time 계산식에서 변수 범위 관련 버그 수정
- CReplicateTargetSelector::SetTargetSelectIntervalOnStarting() 함수상에서
- (long long) ( hour * 3600 * 1000000 ) 부분에서 (long long) (int) 형상으로 계산되므로
- Start Time 에 대한 정확한 계산이 수행되지 않음.
- 이에 따른 수정처리 진행함.
- CHG: Replicate 대상 선택관련 MIN_TARGET_SELECT_INTERVAL 값 변경
- 기존 3 sec 로 할 경우 FHS 간 시간 동기화 관련 문제 발생 가능
- 따라서 해당 MIN 값을 180 sec 로 변경 처리함.
- rc_rmcd.conf 에도 본 내역 적용 처리함.
- CHG: gcc 4.4.5 컴파일시 발생한 Header File include 오류 관련 Header 추가처리
Revision 0451
-------------------
수정일 : 2010-12-09
수정자 : 유희곤
- CHG: Main 버전 정보 변경
- src/Makefile 상에 설정된 기존 Main 버전 3.0.0 을 서비스 1팀 정책에 따른 3.0.1 로 변경
- CHG: Changelog.txt 관련 내역 보강
- Reivision 450 의 작업 내역 보강
Revision 0450
-------------------
수정일 : 2010-12-08
수정자 : 장희준
- CHG: 복제 대상 선택 시간을 설정파일에서 설정할 수 있도록 변경
- rc_rmcd.conf, define_rc_rmcd.h, Worker.cpp, ReplicateTargetSelect.h ReplicateTargetSelect.cpp
- conf 파일상에 TARGET_SELECT_INTERVAL, TARGET_SELECT_INTERVAL_ON_STARTING 항목 추가
- 자세한 사항은 일감 #4999 참고.
- CHG: 불필요한 동적 Library 링크 삭제처리
- src/Makefile LIBS 에 설정된 불필요한 geom, devstat 링크 부분 삭제 처리
- CHG: gcc 4.4.5 컴파일시 발생한 Header File include 오류 관련 Header 추가처리
- lib/Logger.cpp
- BUG: lib/Config.h 구문 오류 수정
- SetDelimiter 함수의 두번째 인자인 string& value = CONFIG_DEFAULT_VALUE_DELIMITER 구문 오류
- string value = CONFIG_DEFAULT_VALUE_DELIMITER 형식으로 수정 처리
Revision 0371
-------------------
수정일 : 2010-10-08
수정자 : 김오종
- BUG: CCompleteTargetInserter::InsertFromQueue(), CCacheCompleteInserter::InsertFromQueue() 함수에서
finame_hash의 중복 여부를 판단하기 위한 쿼리에 nextval()이 호출되어 resource_id의 이상증가 현상이 발생
- 쿼리문중 의도와 상관없는 쿼리구문인 nextval()을 제거 함.
Revision 0337
-------------------
수정일 : 2010-08-11
수정자 : 김오종
- BUG: opendav에서 filename_hash 값이 중복생성(충돌현상)이 될 가능성에 대한 방어코드 추가.
- CCompleteTargetInserter::VarificationInsertData() 함수 추가.
-> filename_hash 값이 중복됐는지 확인 로직추가.
- CCompleteTargetInserter::InsertFromQueue() 함수내에서 VarificationInsertData()함수로 확인 수행.
- CCacheCompleteInserter::InsertFromQueue() 함수내에서 filename_hash 값이 중복됐는지 확인 로직추가.
- BUG: CCompleteTargetInserter::InsertFromQueue()와 CCacheCompleteInserter::InsertFromQueue()의 쿼리의
조건절에 get_content_length 필드 추가.
- 현재 분산 또는 내부캐시 완료된 컨텐츠의 파일크기가 같을경우에만 완료처리 되도록 처리.
- CHG: - defind 된 변수 타입을 기본 타입으로 재선언.
- Data.h에 uint64_t로 정의된 변수를 unsined long long 타입으로 변경.
Revision 0333
-------------------
수정일 : 2010-08-09
수정자 : 김오종
- CHG: vmasd로 부터 수신되는 정보를 이용하여 ProferFHS 정보 update부분 수정.
- vmasd 수신 처리 부분인 CProperFhs::UpdateFhsInfo() 함수 수정.
- opendav 모듈에서 해당 fhs의 디스크 여유용량에 대해 1G이하면 업로드
대상에서 제외되는 로직을 확인 함.
- CHG: Makefile 상의 버전정보 관리 방법 변경
- REVISION 을 추가로 정의하고 Version 표시할때 사용 한다.
Revision 0322
-------------------
수정일 : 2010-08-02
수정자 : 김오종
- BUG: CBaseSocket::Read() 함수상의 문법관련 버그 수정
- 156 Line상의 잘못된 대입 연산자를 비교연산자로 수정처리.
- CHG: Makefile 상의 버전정보 변경.
- 3.0.0.313 => 3.0.0.322
Revision 0313
-------------------
수정일 : 2010-07-15
수정자 : 김오종
- CHG: - Profer FHS 정보 업데이트 중 opendav 비정상 상황에 대한 로그메시지 조정.
- Makefile 버전 조정.
Revision 0311
-------------------
수정일 : 2010-07-15
수정자 : 김오종
- CHG: - vmasd로 부터 수신되는 정보를 이용하여 ProferFHS 선정하는 부분 로직 수정.
-> vmasd 수신 처리 부분인 CProperFhs::UpdateFhsInfo() 함수 수정.
-> opendav 모듈이 기동중이지 않은 경우아래 값을 전송하는 것을 확인하였음.
objFhsInfo.m_nTotalSize = 0;
objFhsInfo.m_nFreeSize = 0;
-> 기존 로직은 어떠한 값이 포함되었건 간에 fhs가 정상이라고 판단 하였으나,
아래 조건이 true인 경우 비정상 상태로간주 하도록 처리 되었음.
* 조건.
if ( objFhsInfo.m_nTotalSize == 0 && objFhsInfo.m_nFreeSize == 0 )
Revision 0285
-------------------
수정일 : 2010-06-30
수정자 : 김오종
- CHG: - CHG: Makefile 상의 버전정보 변경
- R0283에 version이 282로 commit되어 있어 R0285로 변경처리 함.
- Chagelog에 버전 미적용으로 인해 전체 R0285로 변경처리함.
Revision 0282
-------------------
수정일 : 2010-06-24
수정자 : 김태일
- CHG: Replication Target 선정 쿼리 수정
- 선정 기준을 filename_hash => uri로 변경함.
- /user2/ stg/node0 등의 각기 다른 저장소를 가지기 때문에 filename_hash를 기준으로 잡기에 부적합함.
Revision 0281
-------------------
수정일 : 2010-06-22
수정자 : 김태일
- CHG: Makefile 상의 버전정보 변경
- Makefile 상에 설정된 Version 정보를 기존 3.0.0.264 에서 양뱡향 버전 표시 방법으로 변경처리
- 3.0.0.281-생성일시분초 형식
Revision 0280
-------------------
수정일 : 2010-06-22
수정자 : 김태일
- CHG: FHS의 ftsd와 통신이 완료된 상태에서, success list와 fail list를 전달 받는데,
Fail list에 대해서는 리스트에서 삭제 작업이 누락됨.
- 잡 리스트에서 삭제작업(UnregisterJob)을 수행하고, Debug => Error Log로 변경함.
Revision 0264
-------------------
수정일 : 2010-06-01
수정자 : 유희곤
- CHG: Logger Class 의 Init() 호출시 경로가 Symbolic Link인 경우 처리코드 추가
- Log Path 경로가 Directory 가 아닌 Symbolic Link 인 경우로 발생
- 따라서 이러한 경우 기존에는 Error 로 처리되어 Init() 함수 호출이 실패됨.
- 해당 코드를 Symbolic Link 또는 Directory 인 경우에는 정상적으로 처리되도록 수정함.
- CHG: Makefile 상의 버전정보 변경
- Makefile 상에 설정된 Version 정보를 기존 1.0-R0243 에서 양뱡향 버전 표시 방법으로 변경처리
- 3.0.0.264-생성일시분초 형식
Revision 0243
-------------------
수정일 : 2010-05-07
수정자 : 김오종
- CHG: rc간 복제처리를 위해 protocol상에 target tranid정보 전송기능 추가.
- NEW: ProperFHS 클래스에서 disable 된 장비들을 제외하는 로직 추가 필요.
- NEW: mds_sync 개발에 따른 추가개발 이슈.
- CProperFhsUpdater class추가.
- NetCache에서 사용하게될 mds_sync데몬과 연동되는 부분으로
- 신규로 추가된 t_fhs_top_status 테이블에 적당한 FHS를 특정개수(현재10개)이상을
- 기본으로 전체 FHS의 10%에 해당하는 정보를 저장하는 역할을하는 클래스이다.
Revision 0236
-------------------
수정일 : 2010-04-27
수정자 : 김태일
- BUG: PTHREAD_MUTEX_INITIALIZER 코드로 구현된 mutex 초기화 구현 코드를 pthread_mutex_init() 사용으로 변경함.
- 현재 FreeBSD환경에서 동작에 문제가 발견되지 않았으나, 잠재적인 문제가 있으므로 권고사항으로 구현 변경함.
Revision 0231
-------------------
수정일 : 2010-04-15
수정자 : 김태일
- CHG: - process status를 아래와 같은 형태로 변경 표현. 각 약어는 ProcessStatus.cpp에 주석 설명 되어있음.
rc_rmcd: [RS0 RT0 RR0 RC0 CL0 CT0 CJ0 CC0] (rc_rmcd)
- 현재 작업 가능한 스레드 개수를 조회하는 기능에 UpdateTitle() 구문을 추가하여 주기적으로 프로세스 상태가 갱신될 수 있도록 변경.
Revision 0230
-------------------
수정일 : 2010-04-14
수정자 : 김오종
- CHG: Replication 기능의 Replication 개수를 기존 conf파일기준으로 처리하던것을 DB의 값을 참조하도록 기능 변경.
- t_sms_sp_svc_product 에서 replication_count 값을 기준으로 Replication 하도록 변경.
- 고객사 별로 replication_count가 설정되므로, 각 고객사별로 Replication 수를 설정가능하도록 변경.
- NEW: Cache 처리에 대한 기능 추가.
- 현재 버전은 개발 및 성능 테스트를 완료한 버전으로 기존에 Replication 처리기능에 Cache처리기능을 추가했다.
Revision 0177
-------------------
수정일 : 2010-03-23
수정자 : 유희곤
- NEW: SVN 신규 등록
- 기존 svn://doit.solbox.com/work/trunk/rc_rmcd 에 존재하는 개발 완료 소스 (Rev 490)를 SVN 에 정식버전으로 등록함.
- 본 버전은 개발 및 성능 테스트를 완료한 버전으로 현재 버전에서는 Replication 에 대한 처리 기능만을 가짐
- 본 버전은 아직 Cache 처리에 대한 기능을 가지지 않음.
- 현재 버전은 FreeBSD 5.4, 6.2 버전상에서만 테스트되었음.
- 본 버전은 vmasd 정보를 사용하므로 rc_vrcd, rc_cmcd 와 UDP Port 충돌이 날수 있으므로 설치 및 기동시 주의해야 함.
- NEW: History 저장을 위한 Changelog.txt 파일 추가
- 앞으로 각 작업에 대한 로그를 확인하도록 Changelog.txt 파일을 추가
+37
View File
@@ -0,0 +1,37 @@
#****************************************************************************
# Makefile for rc_rmcd ( Replication & Cache Control Daemon)
# -----------------------------------------
#
# begin : 2010/03/09
# 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.
#*****************************************************************************
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
+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;
}
+118
View File
@@ -0,0 +1,118 @@
/***************************************************************************
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 "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__ */
+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__
+70
View File
@@ -0,0 +1,70 @@
#****************************************************************************
# Makefile for Cloud Storage Common Libaray
# -----------------------------------------
#
# begin : 2013/06/04
# copyright : (C) 2013 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.
#*****************************************************************************
# Library info
LIB_NAME = InterCommon
LIB = lib$(LIB_NAME).a
OBJS = Config.o Logger.o BaseSocket.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__
+151
View File
@@ -0,0 +1,151 @@
#include "ReportLog.h"
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.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__ */
+152
View File
@@ -0,0 +1,152 @@
#include "CacheControl.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <list>
#include "CacheThread.h"
#include "Logger.h"
#include "ServiceConfig.h"
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
#define DEFAULT_SLEEP_TIME 2
#define MAX_SLEEP_TIME 5
CCacheControl::CCacheControl( CProcessStatus* pProcessStatus, CCacheJobList* pJobList, CDataQueue<CCacheData>* pQueueCacheComplete )
: m_pProcessStatus( pProcessStatus )
, m_pCacheJob( pJobList )
, m_pQueueCacheComplete( pQueueCacheComplete )
{
// 멤버 변수 초기화
m_threadHandle = 0;
m_uSleep = DEFAULT_SLEEP_TIME;
}
CCacheControl::~CCacheControl()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// thread를 생성하여 Cache 생성 Thread 관련 작업을 수행.
bool CCacheControl::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CCacheControl::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
void* CCacheControl::threadFunc( void* arg )
{
CCacheControl* pObject = reinterpret_cast<CCacheControl *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 요청에 대한 처리속도와 연관된다.
sleep( pObject->m_uSleep );
}
// NEW 2014 - 06 - 23 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CCacheControl::Execute()
{
if( MakeCache() == true )
{
m_uSleep = DEFAULT_SLEEP_TIME;
}
else
{
m_uSleep = MAX_SLEEP_TIME;
}
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
// return true : 처리할 Data 가 계속 존재하는 경우...
// false : 처리할 Data 가 없어 idle 한 경우.
bool CCacheControl::MakeCache()
{
// 1. CProcessStatus에서 생성 가능한 Cache Thread 수 확인
unsigned int nWorkCount = 0;
nWorkCount = m_pProcessStatus->GetValidCacheCount();
if( nWorkCount == 0 )
{
// 생성 가능한 Cache Thrad 수가 없는 경우...
// - false 를 반환할 경우... MAX_SLEEP_TIME 만큼 대기하므로...
// - 빠른 시간내에 재시도할 수 있도록 true 를 반환.
return true;
}
// 2. Job 정보를 가져온다.
// - 생성 가능한 Cache Thread 수 만큼 Job 을 가져온다.
std::list<CCacheData> listWork;
if( m_pCacheJob->GetDataToStart( nWorkCount, listWork ) == false )
{
// 진행할 Cache Job 이 없는 경우.
// - idle 상태
return false;
}
// 3. 가져온 Job 만큼 Cache Thread 를 생성한다.
CCacheData dataCache;
while( listWork.empty() == false )
{
// List 상의 항목을 POP 처리
dataCache = listWork.front();
listWork.pop_front();
// CCacheThread 생성.
// - 이 곳에서는 new로 할당만 한다.
// - CCacheThread가 종료될때 delete this를 하기때문이니 놀라지말것.
CCacheThread* pCacheThread = new CCacheThread( m_pProcessStatus, m_pCacheJob, m_pQueueCacheComplete, dataCache );
// Thread 생성 또는 기동 실패시
// - 해당 Cache 요청을 삭제 처리한다.
if( pCacheThread == NULL || pCacheThread->Start() == false )
{
// 최종 Thread 정리
if( pCacheThread != NULL )
{
delete pCacheThread;
pCacheThread = NULL;
}
_LOG( LERR, "CCacheControl: cache thread start fail.[%s][%s]"
, dataCache.m_requestData.m_strRequestHost.c_str()
, dataCache.m_requestData.m_strFileNameHash.c_str() );
// Job 항목에서 삭제 처리한다.
m_pCacheJob->UnRegister( dataCache );
}
}
// 처리가 완료된 경우.
return true;
}
+83
View File
@@ -0,0 +1,83 @@
/***************************************************************************
Cache create Control class
-----------------------------------------
begin : 2010/03/31
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __CACHE_CONTROL_H__
#define __CACHE_CONTROL_H__
#include <pthread.h>
#include "ProcessStatus.h"
#include "CacheData.h"
#include "CacheJobList.h"
#include "DataQueue.hpp"
// CCacheControl
// Cache Job List 의 정보를 바탕으로 실제 Cache 를 생성하는 Thread 를
// 생성하는 역활을 수행,
// 전체적으로 Cache 생성 업무 Flow 를 제어한다.
// 내부적으로 Thread를 사용하고있다.
class CCacheControl
{
public:
// 생성자
CCacheControl( CProcessStatus* pProcessStatus, CCacheJobList* pJobList, CDataQueue<CCacheData>* pQueueCacheComplete );
// 소멸자
~CCacheControl();
// thread를 생성하여 Cache 생성 Thread 관련 작업을 수행.
bool Start();
private:
// 프로세스 상의 Queue, Thread 상태를 관리하는 CProcessStatus 객체에 대한 포인터
// - 생성자를 통해 전달받음
CProcessStatus * m_pProcessStatus;
// Cache 생성 관련 Job List 객체에 대한 포인터
// - 생성자를 통해 전달받음
CCacheJobList* m_pCacheJob;
// Cache 생성 완료 저장 Queue 에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CCacheData>* m_pQueueCacheComplete;
// 쓰레드 핸들
pthread_t m_threadHandle;
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
unsigned int m_uSleep;
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void Execute();
// 스레드 내에서 각 loop 단위 처리 호출 함수.
// return true : 처리할 Data 가 계속 존재하는 경우...
// false : 처리할 Data 가 없어 idle 한 경우.
bool MakeCache();
};
#endif //__CACHE_CONTROL_H__
+54
View File
@@ -0,0 +1,54 @@
#include "CacheData.h"
// Cache Job 상태 정보 선언
// Cache 대기 상태
#define CACHE_STATUS_READY 0
// Cache Thread 가 할당되어 생성 작업 중인 상태
#define CACHE_STATUS_START 1
// 생성자
CCacheData::CCacheData()
{
m_nStatus = CACHE_STATUS_READY;
}
// 소멸자
CCacheData::~CCacheData()
{
}
// 본 객체의 재사용을 위한 멤버 변수 초기화 함수
void CCacheData::Init()
{
m_requestData.Init();
//m_strUri.clear();
m_strSourceHost.clear();
m_strSourceFilenameHash.clear();
m_strTargetHost.clear();
m_strTargetFilenameHash.clear();
m_nStatus = CACHE_STATUS_READY;
}
void CCacheData::SetStatusReady()
{
m_nStatus = CACHE_STATUS_READY;
}
void CCacheData::SetStatusStart()
{
m_nStatus = CACHE_STATUS_START;
}
bool CCacheData::IsStatusReady()
{
if( m_nStatus == CACHE_STATUS_READY )
return true;
else
return false;
}
+88
View File
@@ -0,0 +1,88 @@
/***************************************************************************
Cache 관련 Data 정보 저장 Class
-----------------------------------------
begin : 2014/07/08
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.3
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 __CACHE_DATA_H__
#define __CACHE_DATA_H__
#include <string>
#include "CacheRequestData.h"
// CCacheData
// Cache 생성 관련 Content 에 대한 모든 정보를 관리하는 Data 객체
class CCacheData
{
public:
// 생성자
CCacheData();
// 소멸자
~CCacheData();
// 본 객체의 재사용을 위한 멤버 변수 초기화 함수
void Init( void );
// 해당 Content 의 작업 상태 정보 변경
// - Cache 생성 대기 상태로 변경
void SetStatusReady();
// 해당 Content 의 작업 상태 정보 변경
// - Cache 생성 작업 중 상태로 변경
void SetStatusStart();
// 해당 Content 의 작업 상태가 CACHE_STATUS_READY 여부 판단
bool IsStatusReady();
public:
// FHS 로 부터 수신된 Cache 생성 request 정보 저장 객체
// - FHS 수신 관련 Worker 및 FimngdSocketControl 모듈에서 data 생성 처리
CCacheRequestData m_requestData;
// FHS 로 부터 수신된 요청의 uri 정보
// - COPY 로 인해 물리 Content 에 연결된 URI 가 여러개인 경우 발생.
// - 따라서 단순 로깅 목적으로의 URI 정보 표시시 오해 발생 가능하므로...
// - URI 정보는 사용하지 않토록 수정함.
//std::string m_strUri;
// Cache 생성 작업시 원본 Content 를 제공할 FHS 정보
// - CacheOrder 모듈에서 생성
std::string m_strSourceHost;
// Cache 생성 작업시 원본 Content 의 filename hash 정보
// - CacheOrder 모듈에서 생성
std::string m_strSourceFilenameHash;
// Cache 생성 작업시 Cache Content 를 저장할 FHS 정보
// - CacheOrder 모듈에서 생성
std::string m_strTargetHost;
// Cache 생성 작업을 통해 최종 생성된 filename hash 정보
// - Cache Thread 모듈에 의해 생성됨
std::string m_strTargetFilenameHash;
private:
// Cache 생성 작업 상태 정보 저장
// 0 : READY ( Thread 할당 대기 )
// 1 : START ( Thread 작업 중 )
int m_nStatus;
};
#endif // __CACHE_DATA_H__
+368
View File
@@ -0,0 +1,368 @@
#include "CacheDelete.h"
#include <errno.h>
#include <string.h>
#include "Logger.h"
#include "Database.h"
#include "Util.h"
#include "ServiceConfig.h"
// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 512
// sec 를 micro-sec 로 단위 변환시 환산 계수.
#define SEC_TO_MICROSEC (1000000LL)
// 삭제 작업시 시작 시간을 설정하기 위한 보정값.
#define CACHE_DELETE_RANGE (60*60*48) // 48시간
// 삭제 주기 ( sec 단위)
#define CAHCE_DEL_CHECK_TERM_SEC (20 * 60) // 20분
// 생성자
CCacheDelete::CCacheDelete()
{
m_threadHandle = 0;
// NEW 2014-06-16 신규 멤버 변수 초기화 코드 추가.
// Init() 에서 RCDB 접속 실패가 발생 가능하므로.. 구형 RCDB 형상을 사용토록 설정.
m_bServiceTableSeparate = false;
}
// 소멸자
CCacheDelete::~CCacheDelete()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 객체 초기화 수행
bool CCacheDelete::Init()
{
// RCDB 접속 정보 Load.
m_rcdbInfo.Load();
// 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 )
{
LOG( LERR, "RCDB connection failed." );
return false;
}
else
{
// 접속 성공한 경우...
LOG( LINF, "RCDB connection test ok..." );
// NEW 2014-06-23 huibong
// 서비스 table 분리된 신규 형상인지.. 구형 형상인지 체크하여
// m_bServiceTableSeparate 변수 설정 처리.
// 해당 변수 초기화는 생성자에서 1차로 수행함.
char szQuery[1024];
snprintf( szQuery, 1023,
"SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource' " );
rcdb.PgDoExec( szQuery );
if( rcdb.PgResult( DataBase::NOT_CLEAR ) < 0 )
{
// Query 수행 결과 오류 발생시...
// - pg_tables 가 존재하지 않을 경우 오류 발생 가능...
// - 이는 RCDB 가 아직 적절하게 구성되지 않았다는 의미이므로... Init()함수를 오류로 처리한다.
LOG( LERR, "RCDB t_dav_resource exist check failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
else
{
// Query 수행이 정상인 경우.
int nResult = rcdb.GetNoTuples();
if( nResult > 0 )
{
// t_dav_resource 테이블이 존재하는 경우...
m_bServiceTableSeparate = false;
_LOG( LINF, "CCacheDelete: t_dav_resource table exist.[Old RCDB Type]" );
}
else
{
// t_dav_resource 테이블이 존재하지 않는 서비스별 테이블 분리 형상인 경우.
m_bServiceTableSeparate = true;
_LOG( LINF, "CCacheDelete: t_dav_resource table not exist.[New RCDB Type]" );
}
}
// RCDB 조회 결과 set 를 clear 처리 후 RCDB 접속 해제.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
}
// thread를 생성하여 업무 flow 를 시작.
bool CCacheDelete::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CCacheDelete::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
void * CCacheDelete::threadFunc( void * arg )
{
CCacheDelete* pObject = reinterpret_cast<CCacheDelete *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
sleep( CAHCE_DEL_CHECK_TERM_SEC );
}
// NEW 2014-06-23 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
// CHG 2014-06-23 huibong
// detach 된 Thread 에 대해 pthread_exit() 호출시
// 기준 할당 자원에 대한 해지 처리가 되지 않아 메모리 누수 발생 가능( FreeBSD ftsd 발생)
// 따라서 이를 해결하기 detach 된 thread 에 대해 pthread_exit() 호출 코드 제거.
//pthread_exit(NULL);
return NULL;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CCacheDelete::Execute()
{
// 구형 t_dav_resource 테이블 사용시
if( m_bServiceTableSeparate == false )
{
DeleteFromOldDB();
}
else
{
// 신형 RCDB 형상 사용시
DeleteFromDB();
}
}
// RCDB t_dav_resource 테이블에서 유효기간이 지난 Hot Content Cache Content 를 삭제 처리
// - 구형 RCDB 지원 목적
bool CCacheDelete::DeleteFromOldDB()
{
// 1. 현재 시간정보를 구한다.( micro-second 단위)
long long timeNow = longtime_now();
if( timeNow < 0 )
{
LOG( LERR, "longtime_now function error [%lld]", timeNow );
return false;
}
// 2. RCDB 와 연결 처리.
DataBase 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 )
{
// RCDB 접속 실패시
LOG( LERR, "RCDB connection failed." );
return false;
}
// 3. t_dav_resource 테이블에서 유효기간이 지난 Cache Content 를 삭제 상태로 update 처리
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
int nResult = 0;
// 처리 대상 시간 구간 계산 (micro-second 단위 )
long long timeStart;
long long timeEnd;
// 현재시간 기준으로 Hot Content Cache 의 보존 만료 시간을 계산.
timeEnd = timeNow - ( CServiceConfig::GetInstance()->GetCacheFileTimeout() * SEC_TO_MICROSEC );
// 조회 시작 시간을 계산.
// - 본 값이 없을 경우... RCDB를 Full Scan 해야 하므로.. RCDB 에 부하 발생 가능.
// - 따라서 보존 만료 시간 이전 48 시간 정도로 조회 범위를 감소시킨다.
timeStart = timeNow - ( ( CServiceConfig::GetInstance()->GetCacheFileTimeout() + CACHE_DELETE_RANGE ) * SEC_TO_MICROSEC );
snprintf(szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
"UPDATE t_dav_resource SET deleted_yn = 'Y' ,get_lastmodified = %lld "
"WHERE is_cache = 'Y' AND deleted_yn = 'N' "
"AND get_lastmodified >= '%lld' "
"AND get_lastmodified <= '%lld';"
, timeNow, timeStart, timeEnd
);
// Query 실행
rcdb.PgDoExec(szQuery);
// Update 처리 결과 Row 수 확인.
nResult = rcdb.GetCmdTuples();
if( nResult > 0 )
{
_LOG( LINF, "CCacheDelete: t_dav_resource delete count[%d]", nResult );
}
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
// RCDB t_meta_xxx 테이블에서 유효기간이 지난 Hot Content Cache Content 를 삭제 처리
// - 신형 RCDB 지원 목적
bool CCacheDelete::DeleteFromDB()
{
// 1. 현재 시간정보를 구한다.( micro-second 단위)
long long timeNow = longtime_now();
if( timeNow < 0 )
{
LOG( LERR, "longtime_now function error [%lld]", timeNow );
return false;
}
// 2. RCDB 와 연결 처리.
DataBase 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 )
{
// RCDB 접속 실패시
LOG( LERR, "RCDB connection failed." );
return false;
}
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
int nResult = 0;
// 3. RCDB t_sms_sp_svc_product 테이블에 저장된 서비스 정보를 조회한다.
snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
"SELECT sp_svc_tran_id FROM t_sms_sp_svc_product ORDER BY sp_svc_tran_id DESC "
);
// Query 실행
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// Query 오류 발생시
// - RCDB 연결 실패 또는
// - t_sms_sp_svc_product 테이블이 존재하지 않을 경우.
LOG( LERR, "RCDB t_sms_sp_svc_product query error.[%s][%s]"
, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
// 조회된 각 고객사 정보 확인
nResult = rcdb.GetNoTuples();
if( nResult <= 0 )
{
// 고객사가 존재하지 않는 경우
_LOG( LINF, "CCacheDelete: service not found from t_sms_sp_svc_product.[%d]"
, nResult );
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
// 조회된 고객사 정보를 변수에 저장.
std::vector<std::string> vecService;
std::string strTemp;
for( int i = 0; i < nResult; i++ )
{
strTemp = rcdb.GetValue( i, 0 );
if( strTemp.size() > 0 )
vecService.push_back( strTemp );
}
rcdb.PgClear();
// 변수에 저장된 서비스 정보의 수를 최종 확인
if( vecService.empty() == true )
{
// 유효한 서비스 정보가 존재하지 않는 경우.
_LOG( LINF, "CCacheDelete: service not found from t_sms_sp_svc_product." );
rcdb.PgCloseDB();
return true;
}
// 4. 루프를 돌면서
// 각 서비스별 t_meta_xx 테이블에서 유효기간이 지난 Cache Content 를 삭제 상태로 update 처리
// 처리 대상 시간 구간 계산 (micro-second 단위 )
long long timeStart;
long long timeEnd;
// 현재시간 기준으로 Hot Content Cache 의 보존 만료 시간을 계산.
timeEnd = timeNow - ( CServiceConfig::GetInstance()->GetCacheFileTimeout() * SEC_TO_MICROSEC );
// 조회 시작 시간을 계산.
// - 본 값이 없을 경우... RCDB를 Full Scan 해야 하므로.. RCDB 에 부하 발생 가능.
// - 따라서 보존 만료 시간 이전 48 시간 정도로 조회 범위를 감소시킨다.
timeStart = timeNow - ( ( CServiceConfig::GetInstance()->GetCacheFileTimeout() + CACHE_DELETE_RANGE ) * SEC_TO_MICROSEC );
std::vector< std::string >::const_iterator it;
for( it = vecService.begin(); it != vecService.end(); ++it )
{
snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
"UPDATE t_meta_%s SET deleted_yn = 'Y' ,get_lastmodified = %lld "
"WHERE is_cache = 'Y' AND deleted_yn = 'N' "
"AND get_lastmodified >= '%lld' "
"AND get_lastmodified <= '%lld';"
, it->c_str()
, timeNow, timeStart, timeEnd
);
// Query 실행
rcdb.PgDoExec( szQuery );
// Update 처리 결과 Row 수 확인.
nResult = rcdb.GetCmdTuples();
if( nResult > 0 )
{
_LOG( LINF, "CCacheDelete: t_meta_%s delete count[%d]", it->c_str(), nResult );
}
rcdb.PgClear();
}
// 작업이 완료된 경우... RCDB 연결 해제.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
+76
View File
@@ -0,0 +1,76 @@
/***************************************************************************
Cache content delete class
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __CACHE_DELETE_H__
#define __CACHE_DELETE_H__
#include <pthread.h>
#include "RcdbInfo.h"
// CCacheDelete
// RCDB 에 저장된 Hot Content Cache Content 의 Timeout 여부를 판단하여...
// 설정된 유효 기간이 초과된 경우.. 삭제 처리를 수행한다.
class CCacheDelete
{
public :
// 생성자
CCacheDelete();
// 소멸자
~CCacheDelete();
// 객체 초기화 수행
bool Init();
// thread를 생성하여 업무 flow 를 시작.
bool Start();
private :
// 스레드 함수
static void* threadFunc( void* arg );
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void Execute();
// RCDB t_dav_resource 테이블에서 유효기간이 지난 Hot Content Cache Content 를 삭제 처리
// - 구형 RCDB 지원 목적
bool DeleteFromOldDB();
// RCDB t_meta_xxx 테이블에서 유효기간이 지난 Hot Content Cache Content 를 삭제 처리
// - 신형 RCDB 지원 목적
bool DeleteFromDB();
private :
// 내부 Thread 에 대한 ID
pthread_t m_threadHandle;
// RCDB 접속 정보를 저장할 변수.
CRcdbInfo m_rcdbInfo;
// New 2014-06-23 huibong
// RCDB 형상변경에 따라 서비스별 Table 분리 여부 판단 flag
// TODO: 향후 신규 RCDB 형상 적용 완료 후 본 항목 제거 필요.
// - false : 기존 t_dav_resource 테이블 사용.
// - ture : 신규 RCDB 형상 (서비스별 meta table 분리) 사용.
bool m_bServiceTableSeparate;
};
#endif // __CACHE_DELETE_H__
+581
View File
@@ -0,0 +1,581 @@
#include "CacheInsert.h"
#include <errno.h>
#include <string.h>
#include "Database.h"
#include "Logger.h"
#include "Util.h"
// 처리 loop 간의 sleep time 정의
#define DEFAULT_SLEEP_TIME 3
// RCDB 접속 실패 또는 Query 실패 등의 상황 발생시
#define MAX_SLEEP_TIME 30
// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 2048
// 생성자
// param [IN] pQueueCacheComplete Cache 생성 완료 Queue 에 대한 포인터
// param [IN] pListCacheJob Cache 생성 관련 Job List 객체에 대한 포인터
CCacheInsert::CCacheInsert( CDataQueue<CCacheData>* pQueueCacheComplete, CCacheJobList* pCacheJob )
: m_pQueueCacheComplete( pQueueCacheComplete )
, m_pCacheJob( pCacheJob )
{
m_threadHandle = 0;
m_uSleep = DEFAULT_SLEEP_TIME;
// NEW 2014-07-07 신규 멤버 변수 초기화 코드 추가.
// Init() 에서 RCDB 접속 실패가 발생 가능하므로.. 구형 RCDB 형상을 사용토록 설정.
m_bServiceTableSeparate = false;
}
// 소멸자
CCacheInsert::~CCacheInsert()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 객체 초기화 수행
bool CCacheInsert::Init()
{
// 생성자로 전달받은 각 객체에 대한 포인터는 유효하다가 판단하고....
// 각 멤버변수에 대한 검사는 수행하지 않는다.
// RCDB 접속 정보 Load.
m_rcdbInfo.Load();
// 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 )
{
LOG( LERR, "RCDB connection failed." );
return false;
}
else
{
// 접속 성공한 경우...
LOG( LINF, "RCDB connection test ok..." );
// NEW 2014-06-23 huibong
// 서비스 table 분리된 신규 형상인지.. 구형 형상인지 체크하여
// m_bServiceTableSeparate 변수 설정 처리.
// 해당 변수 초기화는 생성자에서 1차로 수행함.
char szQuery[1024];
snprintf( szQuery, 1023,
"SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource' " );
rcdb.PgDoExec( szQuery );
if( rcdb.PgResult( DataBase::NOT_CLEAR ) < 0 )
{
// Query 수행 결과 오류 발생시...
// - pg_tables 가 존재하지 않을 경우 오류 발생 가능...
// - 이는 RCDB 가 아직 적절하게 구성되지 않았다는 의미이므로... Init()함수를 오류로 처리한다.
LOG( LERR, "RCDB t_dav_resource exist check failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
else
{
// Query 수행이 정상인 경우.
int nResult = rcdb.GetNoTuples();
if( nResult > 0 )
{
// t_dav_resource 테이블이 존재하는 경우...
m_bServiceTableSeparate = false;
_LOG( LINF, "CCacheInsert: t_dav_resource table exist.[Old RCDB Type]" );
}
else
{
// t_dav_resource 테이블이 존재하지 않는 서비스별 테이블 분리 형상인 경우.
m_bServiceTableSeparate = true;
_LOG( LINF, "CCacheInsert: t_dav_resource table not exist.[New RCDB Type]" );
}
}
// RCDB 조회 결과 set 를 clear 처리 후 RCDB 접속 해제.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
}
// thread를 생성하여 업무 flow 를 시작.
bool CCacheInsert::Start()
{
int nRet = ::pthread_create( &m_threadHandle, 0, CCacheInsert::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// 스레드 함수
void* CCacheInsert::threadFunc( void* arg )
{
CCacheInsert* pObject = reinterpret_cast<CCacheInsert *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 1회 수행 후 지정된 시간 만큼 대기
sleep( pObject->m_uSleep );
}
// NEW 2014-07-07 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CCacheInsert::Execute()
{
// Cache complete queue 상에 처리할 Data 가 존재하는 경우.
if( m_pQueueCacheComplete->GetSize() > 0 )
{
bool bResult;
// t_dav_resource 테이블 사용 형상인 경우.
if( m_bServiceTableSeparate == false )
{
bResult = InsertToOldDB();
}
else
{
// 서비스별 Table 이 분린된 신규 형상인 경우.
bResult = InsertToDB();
}
// 처리 결과에 따라 loop sleep time 조정
if( bResult == true )
m_uSleep = DEFAULT_SLEEP_TIME;
else
{
// RCDB 연결 실패, Queury 수행 실패 등의 오류 발생시
m_uSleep = MAX_SLEEP_TIME;
}
}
else
{
// 처리할 Data 가 존재하지 않는 경우.
m_uSleep = DEFAULT_SLEEP_TIME;
}
}
// Cache 생성이 완료된 Job 내역을 구형 RCDB 형상에 INSERT 처리
// - RCDB 에 저장이 완료된 후
// - 해당 내역을 Job 관리 항목에서 삭제 처리해야 추가 요청을 수신할 수 있다.
bool CCacheInsert::InsertToOldDB()
{
// 1. RCDB 에 접속
DataBase 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 )
{
// RCDB 접속이 실패
LOG( LERR, "RCDB connection failed." );
return false;
}
// 2. Queue Pop 처리
// queue 에서 꺼낸 정보를 저장하기 위한 변수
CCacheData resultData;
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
// Query 수행 결과값 저장을 위한 변수..
int nResult = 0;
while( m_pQueueCacheComplete->GetSize() > 0 )
{
// queue 에 저장된 첫번째 data 복사 (queue 에서 삭제된 상태 아님)
resultData = m_pQueueCacheComplete->Front();
// 3. RCDB 에 저장된 해당 Content 의 원본 정보를 조회
// - 이미 삭제가 되었을 수도 있지만....
// RCDB 에 등록하지 않을 경우... 생성된 content 의 자동 삭제가 불가능하므로...
// 조회시 삭제 여부는 조건에 포함시키지 않는다.
// - uri 정보를 이용하여 조회시 특수문자 포함으로 문제가 발생 가능하고..
// 실제 작업을 수행한 원본이 아닐 수 있으므로... (RCDB key 값 문제)
// Source FHS 와 filname_hash 정보를 이용하여 조회 수행
sprintf( szQuery,
"SELECT "
" sp_user_seq, sp_svc_tran_id, '%s', " // 생성된 FHS host_name
" resource_type, uri, filename_org, '%s', " // 생성된 filename_hash
" depth, display_name, get_content_language, "
" get_content_length, get_content_type, get_etag, "
" %lld, creation_date, " // get_lastmodified
" lock_discovery, source, supported_lock, deleted_yn, 'Y', file_lastmodified "
"FROM t_dav_resource "
"WHERE filename_hash = '%s' " // 생성시 사용한 filename_hash
"AND get_content_length = %lld " // 생성시 사용한 get_content_length
"AND host_name = '%s' ;" // 생성시 사용한 FHS host_name
, resultData.m_strTargetHost.c_str() // 생성된 FHS host_name
, resultData.m_strTargetFilenameHash.c_str() // 생성된 filename_hash
, longtime_now() // get_lastmodified
, resultData.m_strSourceFilenameHash.c_str() // 생성시 사용한 filename_hash
, resultData.m_requestData.m_uContentLength // 생성시 사용한 get_content_length
, resultData.m_strSourceHost.c_str() // 생성시 사용한 FHS host_name
);
// Query 수행 및 결과 확인
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// RCDB t_dav_resource 테이블 조회 실패시
// 우선 DB 연결 문제일 수 있으므로...
// RCDB 재접속 시도.
rcdb.PgClear();
rcdb.PgCloseDB();
sleep( 1 );
if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
{
// RCDB 접속 실패 문제인 경우..
// - 본 처리 함수 종료 처리하여 다음 loop 에서 재시도토록 처리.
LOG( LERR, "select query fail because RCDB connection error. [%d][%s]"
, nResult, rcdb.GetErrorMessage().c_str() );
rcdb.PgCloseDB();
return false;
}
else
{
// query 실패시
// - 관련 내역 로깅
// - queue 에서 해당 정보를 삭제, Job 에서 해당 정보 삭제 후
// - 루프 계속 수행한다.
LOG( LERR, "select query fail. queue forced pop.[%d][%s][%s]"
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
m_pQueueCacheComplete->Pop();
m_pCacheJob->UnRegister( resultData );
continue;
}
}
// 결과 Row 수 확인.
nResult = rcdb.GetNoTuples();
if( nResult <= 0 )
{
// Cache 생성된 content 가 RCDB 에 존재하지 않는 경우...
// - RCDB 에 저장하지 않고 그냥 버린다.
// - 이로 인해 FHS 에 meta 기준으로 삭제 불가능한 파일이 남게 된다.
_LOG( LINF, "Cache source content[%s] not found in RCDB."
, resultData.m_strSourceFilenameHash.c_str() );
rcdb.PgClear();
m_pQueueCacheComplete->Pop();
m_pCacheJob->UnRegister( resultData );
continue;
}
// 조회 결과는
// COPY 처리로 인해 N 개의 URI 가 작업에 사용된 content 를 가르킬 수 있으므로...
// N 개가 조회 될 수 있다.
// 따라서 COPY 된 URI 각각에 대해 Cache 정보를 등록해야 한다.
// 조회된 N개 row 정보를 저장하기 위한 vector
std::vector< std::vector<string> > srcarry;
std::string strResult;
for( int j = 0; j < nResult; j++ )
{
// 조회된 1개의 Row 정보를 저장하기 위한 vector
std::vector<string> srcinfo;
for( int k = 0; k < 21; k++ )
{
strResult = rcdb.GetValue( j, k );
strResult = stringreplace( strResult, "'", "''" );
srcinfo.push_back( strResult );
}
srcarry.push_back( srcinfo );
}
// Select Query 의 결과를 Clear
rcdb.PgClear();
// 4. 조회된 N 개의 정보를 바탕으로 RCDB 에 INSERT 처리 수행.
while( srcarry.empty() == false )
{
std::vector<string> srcinfo = srcarry.back();
srcarry.pop_back();
sprintf( szQuery,
"INSERT INTO t_dav_resource "
"( "
" sp_user_seq, sp_svc_tran_id, "
" host_name, "
" resource_type, uri, filename_org, filename_hash, "
" depth, display_name, get_content_language, "
" get_content_length, get_content_type, "
" get_etag, get_lastmodified, creation_date, "
" lock_discovery, source, supported_lock, deleted_yn, is_cache ,file_lastmodified) "
"VALUES "
" ( "
" %s, %s, "
" '%s', "
" %s, '%s', '%s', '%s', "
" %s, '%s', '%s', "
" %s, '%s', "
" '%s', %s, %s, "
" '%s', %s, '%s', '%s', '%s', %s );", // is_cache = Y
srcinfo[0].c_str(), srcinfo[1].c_str(), srcinfo[2].c_str(), srcinfo[3].c_str(),
srcinfo[4].c_str(), srcinfo[5].c_str(), srcinfo[6].c_str(), srcinfo[7].c_str(),
srcinfo[8].c_str(), srcinfo[9].c_str(), srcinfo[10].c_str(), srcinfo[11].c_str(),
srcinfo[12].c_str(), srcinfo[13].c_str(), srcinfo[14].c_str(), srcinfo[15].c_str(),
srcinfo[16].c_str(), srcinfo[17].c_str(), srcinfo[18].c_str(), srcinfo[19].c_str(), srcinfo[20].c_str()
);
// INSERT query 수행
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::CLEAR );
if( nResult < 0 )
{
// RCDB INSERT 작업시 오류가 발생한 경우...
// - DB 연결 문제 역시 바로 앞에서 체크 했으므로 아닐 가능성이 큼.
// - 가장 큰 오류는 Query 오류일 가능성이 큼.
// - 루프 계속 수행
LOG( LERR, "insert query fail. queue forced pop.[%d][%s][%s]"
, nResult, rcdb.GetErrorMessage().c_str(), szQuery );
}
else
{
// 성공시
// PgResult 호출시 CLEAR 옵션을 사용했으므로....내부 결과 set 포인터가 NULL 처리됨.
// 따라서 수행 row 수 확인을 위해 GetCmdTuples() 함수를 호출해서는 안됨.
_LOG( LINF, "Cache insert to RCDB. query result[%d] [%s][%llu]"
, nResult, resultData.m_strTargetFilenameHash.c_str()
, resultData.m_requestData.m_uContentLength );
}
// Query 재사용을 위한 clear 처리.
rcdb.PgClear();
}
// 5. 해당 Content 에 대한 작업이 완료된 경우..
// - Queue , Job 항목에서 삭제 처리.
m_pQueueCacheComplete->Pop();
m_pCacheJob->UnRegister( resultData );
}
// queue 에 저장된 정보 처리 작업이 완료된 경우...
// - RCDB 연결 해제 처리.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
// Cache 생성이 완료된 Job 내역을 신규 RCDB 형상에 INSERT 처리
// - RCDB 에 저장이 완료된 후
// - 해당 내역을 Job 관리 항목에서 삭제 처리해야 추가 요청을 수신할 수 있다.
bool CCacheInsert::InsertToDB()
{
// 1. RCDB 에 접속
DataBase 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 )
{
// RCDB 접속이 실패
LOG( LERR, "RCDB connection failed." );
return false;
}
// 2. Queue Pop 처리
// queue 에서 꺼낸 정보를 저장하기 위한 변수
CCacheData resultData;
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
// Query 수행 결과값 저장을 위한 변수..
int nResult = 0;
while( m_pQueueCacheComplete->GetSize() > 0 )
{
// queue 에 저장된 첫번째 data 복사 (queue 에서 삭제된 상태 아님)
resultData = m_pQueueCacheComplete->Front();
// 3. RCDB 에 저장된 해당 Content 의 원본 정보를 조회
// - 이미 삭제가 되었을 수도 있지만....
// RCDB 에 등록하지 않을 경우... 생성된 content 의 자동 삭제가 불가능하므로...
// 조회시 삭제 여부는 조건에 포함시키지 않는다.
// - uri 정보를 이용하여 조회시 특수문자 포함으로 문제가 발생 가능하고..
// 실제 작업을 수행한 원본이 아닐 수 있으므로... (RCDB key 값 문제)
// Source FHS 와 filname_hash 정보를 이용하여 조회 수행
// - 신규 RCDB 형상 지원 및 미사용 컬럼 제거 처리
// CHG 2015-02-12 huibong
// 신규 RCDB 형상에 맞도록 처리 Flow 단순화 (#21354)
// - RCDB 구조가 pgpool -> streaming replicaton 구조로 변경됨
// - 이에 따라 기존 SELECT 후 INSERT, 검수, DELETE flow 에 대한 단순화 가능함.
sprintf( szQuery,
"INSERT INTO t_meta_%s "
"( "
" sp_user_seq, sp_svc_tran_id, host_name, "
" resource_type, uri, filename_org, filename_hash, "
" depth, display_name, "
" get_content_length, get_content_type, "
" get_lastmodified, creation_date, "
" source, deleted_yn, is_cache, file_lastmodified "
") "
"SELECT "
" sp_user_seq, sp_svc_tran_id, '%s', " // 생성된 FHS host_name
" resource_type, uri, filename_org, '%s', " // 생성된 filename_hash
" depth, display_name, "
" get_content_length, get_content_type, "
" %lld, creation_date, " // get_lastmodified
" source, deleted_yn, 'Y', file_lastmodified "
"FROM t_meta_%s " // 신규 RCDB 형상
"WHERE filename_hash = '%s' " // 생성시 사용한 filename_hash
"AND get_content_length = %lld " // 생성시 사용한 get_content_length
"AND host_name = '%s' ;" // 생성시 사용한 FHS host_name
, resultData.m_requestData.m_strServiceSeq.c_str() // 신규 RCDB 형상 지원
, resultData.m_strTargetHost.c_str() // 생성된 FHS host_name
, resultData.m_strTargetFilenameHash.c_str() // 생성된 filename_hash
, longtime_now() // get_lastmodified
, resultData.m_requestData.m_strServiceSeq.c_str() // 신규 RCDB 형상 지원
, resultData.m_strSourceFilenameHash.c_str() // 생성시 사용한 filename_hash
, resultData.m_requestData.m_uContentLength // 생성시 사용한 get_content_length
, resultData.m_strSourceHost.c_str() // 생성시 사용한 FHS host_name
);
// Query 수행 및 결과 확인
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// Query 수행 실패시
// 우선 DB 연결 문제일 수 있으므로...
// RCDB 재접속 시도.
std::string strPreviousErrorMessage = rcdb.GetErrorMessage();
rcdb.PgClear();
rcdb.PgCloseDB();
sleep( 1 );
if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
{
// RCDB 접속 실패 문제인 경우..
// - 본 처리 함수 종료 처리하여 다음 loop 에서 재시도토록 처리.
LOG( LERR, "Query fail because RCDB connection error. [%d][%s]"
, nResult, rcdb.GetErrorMessage().c_str() );
rcdb.PgCloseDB();
return false;
}
else
{
// query 실패시
// - 관련 내역 로깅
// - queue 에서 해당 정보를 삭제, Job 에서 해당 정보 삭제 후
// - 루프 계속 수행한다.
LOG( LERR, "insert-select query fail. queue forced pop.[%d][%s][%s]"
, nResult, strPreviousErrorMessage.c_str(), szQuery );
rcdb.PgClear();
m_pQueueCacheComplete->Pop();
m_pCacheJob->UnRegister( resultData );
continue;
}
}
// PgResult 호출시 NOT_CLEAR 옵션을 사용했으므로....INSERT 수행 row 수 확인을 위해 GetCmdTuples() 함수를 호출가능.
nResult = rcdb.GetCmdTuples();
if( nResult > 0 )
{
// COPY 처리로 인해 N 개의 URI 가 작업에 사용된 content 를 가르킬 수 있으므로...
// N 개가 나타날 수 있다.
_LOG( LINF, "Cache insert to RCDB. [%d] row. [%s][%s][%llu]"
, nResult
, resultData.m_strTargetHost.c_str()
, resultData.m_strTargetFilenameHash.c_str()
, resultData.m_requestData.m_uContentLength
);
}
else
{
// INSERT 처리된 row 가 없는 경우....
// - 생성된 cache 파일은 고아 파일이 됨.
_LOG( LWAR, "cache rcdb Insert origin not found. orgin[%s|%s|%llu] orphan[%s|%s|%llu]"
, resultData.m_strSourceHost.c_str()
, resultData.m_strSourceFilenameHash.c_str()
, resultData.m_requestData.m_uContentLength
, resultData.m_strTargetHost.c_str()
, resultData.m_strTargetFilenameHash.c_str()
, resultData.m_requestData.m_uContentLength
);
}
// Query 의 결과 Clear
rcdb.PgClear();
// 해당 Content 에 대한 작업이 완료된 경우..
// - Queue , Job 항목에서 삭제 처리.
m_pQueueCacheComplete->Pop();
m_pCacheJob->UnRegister( resultData );
}
// queue 에 저장된 정보 처리 작업이 완료된 경우...
// - RCDB 연결 해제 처리.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
+89
View File
@@ -0,0 +1,89 @@
/***************************************************************************
생성된 Cache content 정보를 RCDB 에 저장처리
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __CACHE_INSERT_H__
#define __CACHE_INSERT_H__
#include <pthread.h>
#include "DataQueue.hpp"
#include "CacheData.h"
#include "CacheJobList.h"
#include "RcdbInfo.h"
// CCacheInsert
// queue 에 저장된 Cache 생성이 완료된 Content 정보를 RCDB 에 INSERT 처리
// 내부적으로 Thread를 사용
class CCacheInsert
{
public:
// 생성자
// param [IN] pQueueCacheComplete Cache 생성 완료 Queue 에 대한 포인터
// param [IN] pListCacheJob Cache 생성 관련 Job List 객체에 대한 포인터
CCacheInsert( CDataQueue<CCacheData>* pQueueCacheComplete, CCacheJobList* pCacheJob );
// 소멸자
~CCacheInsert();
// 객체 초기화 수행
bool Init();
// thread를 생성하여 업무 flow 를 시작.
bool Start();
private:
// Cache 생성 완료 저장 Queue 에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CCacheData>* m_pQueueCacheComplete;
// Cache 생성 관련 Job List 객체에 대한 포인터
// - 생성자를 통해 전달받음
CCacheJobList* m_pCacheJob;
// 내부 Thread 에 대한 ID
pthread_t m_threadHandle;
// RCDB 접속 정보를 저장할 변수.
CRcdbInfo m_rcdbInfo;
// New 2014-07-09 huibong
// RCDB 형상변경에 따라 서비스별 Table 분리 여부 판단 flag
// TODO: 향후 신규 RCDB 형상 적용 완료 후 본 항목 제거 필요.
// - false : 기존 t_dav_resource 테이블 사용.
// - ture : 신규 RCDB 형상 (서비스별 meta table 분리) 사용.
bool m_bServiceTableSeparate;
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
unsigned int m_uSleep;
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void Execute();
// Cache 생성이 완료된 Job 내역을 구형 RCDB 형상에 INSERT 처리
bool InsertToOldDB();
// Cache 생성이 완료된 Job 내역을 신규 RCDB 형상에 INSERT 처리
bool InsertToDB();
};
#endif // __CACHE_INSERT_H__
+238
View File
@@ -0,0 +1,238 @@
#include "CacheJobList.h"
#include <stdio.h>
#include "Logger.h"
// 생성자
CCacheJobList::CCacheJobList()
{
pthread_mutex_init( &m_mutexMap, NULL );
}
// 소멸자
CCacheJobList::~CCacheJobList()
{
pthread_mutex_destroy( &m_mutexMap );
}
// 등록된 job 의 갯수 반환.
unsigned int CCacheJobList::GetSize()
{
return m_mapJob.size();
}
// 해당 Content 에 대한 Job 이 존재하는지 여부 판단.
// true : 이미 존재
// false : 존재 안하거나.. 인자값 오류
bool CCacheJobList::IsExist( CCacheRequestData & data )
{
// 전달받은 정보를 이용하여 map 조회를 위한 key 값 생성
std::string strKey;
if( GetKey( data, strKey ) == false )
{
// Key 생성 실패시
return false;
}
// 생성된 Key 를 기준으로 존재 여부 확인
bool bResult;
std::map<std::string, CCacheData>::const_iterator pos;
// lock 처리 후 조회
pthread_mutex_lock( &m_mutexMap );
pos = m_mapJob.find( strKey );
if( pos != m_mapJob.end() )
{
// 해당 Key 값이 존재하면...
bResult = true;
}
else
{
// 해당 Key 값이 존재하지 않는 경우.
bResult = false;
}
// lock 해제
pthread_mutex_unlock( &m_mutexMap );
return bResult;
}
// 해당 Content 에 대해 등록된 job 삭제
void CCacheJobList::UnRegister( CCacheData & data )
{
// 전달받은 정보를 이용하여 map 조회를 위한 key 값 생성
std::string strKey;
if( GetKey( data.m_requestData, strKey ) == false )
{
// Key 생성 실패시
return;
}
// 생성된 Key 를 기준으로 존재 여부 확인
std::map<std::string, CCacheData>::iterator pos;
// lock 처리 후 조회
pthread_mutex_lock( &m_mutexMap );
pos = m_mapJob.find( strKey );
if( pos != m_mapJob.end() )
{
// 해당 Key 값이 존재하면...erase 를 이용하여 삭제 처리
m_mapJob.erase( pos );
}
// lock 해제
pthread_mutex_unlock( &m_mutexMap );
return;
}
// 해당 Content 에 대한 Job 등록
// true : 등록 성공
// false : 등록 실패
bool CCacheJobList::Register( CCacheData & data )
{
// 전달받은 정보를 이용하여 map 조회를 위한 key 값 생성
std::string strKey;
if( GetKey( data.m_requestData, strKey ) == false )
{
// Key 생성 실패시
return false;
}
// 생성된 Key 를 기준으로 존재 여부 확인
bool bResult;
std::map<std::string, CCacheData>::const_iterator pos;
// lock 처리 후 조회
pthread_mutex_lock( &m_mutexMap );
pos = m_mapJob.find( strKey );
if( pos != m_mapJob.end() )
{
// 해당 Key 값이 이미 존재하는 경우. 등록 실패 처리
bResult = false;
}
else
{
// 해당 정보가 존재하지 않는 경우.
// map 에 등록 처리.
if( m_mapJob.insert( std::pair<const std::string, CCacheData>( strKey, data ) ).second == false )
{
// insert 작업 실패시
bResult = false;
}
else
{
// insert 작업 성공시
bResult = true;
}
}
// lock 해제
pthread_mutex_unlock( &m_mutexMap );
// 처리 결과에 따른 로깅 처리
if( bResult == false )
{
LOG( LERR, "cache data insert fail to job [%s][%s]"
, data.m_requestData.m_strRequestHost.c_str()
, data.m_requestData.m_strFileNameHash.c_str() );
}
return bResult;
}
// 저장된 job 중에서 상태 정보가 CACHE_STATUS_READY 인 job 을
// 전달된 listData 에 최대 nMaxCount 값만큼 저장하여 반환한다.
// - 전달된 job 은 중복 추출을 방지하기 위해 상태정보를 CACHE_STATUS_START 변경처리한다.
// @param [IN] nMaxCout 추출 가능한 최대 갯수
// @param [OUT] 추출된 job 정보를 저장하기 위한 List
// @return false 추출 가능한 항목이 없을 경우.
// true 그 외
bool CCacheJobList::GetDataToStart( const unsigned int nMaxCount, std::list<CCacheData>& listData )
{
// 인자로 전달되어 저장 역활을 담당하는 listData 객체 초기화
listData.clear();
// 전달받은 인자값 검사
if( nMaxCount == 0 )
{
LOG( LERR, "request job count 0. check flow [%u]", nMaxCount );
return false;
}
std::map<std::string, CCacheData>::iterator it;
// lock 설정
pthread_mutex_lock( &m_mutexMap );
for( it = m_mapJob.begin(); it != m_mapJob.end(); ++it )
{
// CACHE_STATUS_READY 상태인지 확인
if( it->second.IsStatusReady() == true )
{
// 해당 job 의 상태를 CACHE_STATUS_START 로 변경 처리
it->second.SetStatusStart();
// 전달받은 listData 에 해당 정보 push 처리
listData.push_back( it->second );
// 요청한 갯수만큼 추출했는지 확인
if( listData.size() >= nMaxCount )
break;
}
}
// lock 해제
pthread_mutex_unlock( &m_mutexMap );
if( listData.empty() == true )
return false;
else
return true;
}
// 전달받은 정보를 바탕으로 map 에 사용할 Key 값을 생성한다.
// key 값은 동일 Contnt 중복 방지를 위해
// content_length:filename_hash(경로정보를 제외한 Only hash name) 형식으로 구성한다.
// @param data [IN] content length 및 filename hash 정보가 저장된 CCacheRequestData 객체 참조
// @param strKey [OUT] 생성된 key 값.
bool CCacheJobList::GetKey( CCacheRequestData & data, std::string& strKey )
{
// Key 값 생성을 위한 값에 대한 검사
if( data.m_uContentLength == 0 )
{
LOG( LERR, "invalid key parameter. length[%llu] hash[%s]"
, data.m_uContentLength, data.m_strFileNameHash.c_str());
return false;
}
std::string strHashName;
if( data.GetOnlyHashFileName( strHashName ) == false )
{
LOG( LERR, "invalid key parameter. hash[%s]"
,data.m_strFileNameHash.c_str() );
return false;
}
else
{
char tempBuffer[256];
sprintf( tempBuffer, "%llu", data.m_uContentLength );
strKey = tempBuffer;
strKey.append( ":" );
strKey.append( strHashName );
return true;
}
}
+93
View File
@@ -0,0 +1,93 @@
/***************************************************************************
Cache 생성 관련 Job List 관리 class
-----------------------------------------
begin : 2014/07/08
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __CACHE_JOB_LIST_H__
#define __CACHE_JOB_LIST_H__
#include <string>
#include <map>
#include <list>
#include <pthread.h>
#include "CacheRequestData.h"
#include "CacheData.h"
// CCacheJobList class
// Hot Content cache 생성 관련하여
// 대기 중인 content 목록과 현재 Thread 를 통해서 진행 중인 Content 목록을 관리한다.
// 본 클래스의 목적은
// 동일 Content 에 대해 연속된 Cache 생성 요청으로 인한 중복 작업으로 인해 발생 가능한
// 잘못된 업무 Flow 발생을 방지하기 위해 존재한다.
// 작업 등록 : CacheOrder 모듈
// 작업 수행 : CacheControl 모듈 및 각 Cache Thread
// 작업 삭제 : CacheInsert 모듈에서 RCDB 작업 완료 후 삭제 처리.
class CCacheJobList
{
public :
// 생성자.
CCacheJobList();
// 소멸자.
~CCacheJobList();
// 등록된 job 의 갯수 반환.
unsigned int GetSize( void );
// 해당 Content 에 대한 Job 이 존재하는지 여부 판단.
// true : 이미 존재
// false : 존재 안하거나.. 인자값 오류
bool IsExist( CCacheRequestData & data );
// 해당 Content 에 대한 Job 등록
// true : 등록 성공
// false : 등록 실패
bool Register( CCacheData & data );
// 해당 Content 에 대해 등록된 job 삭제
void UnRegister( CCacheData & data );
// 저장된 job 중에서 상태 정보가 CACHE_STATUS_READY 인 job 을
// 전달된 listData 에 최대 nMaxCount 값만큼 저장하여 반환한다.
// - 전달된 job 은 중복 추출을 방지하기 위해 상태정보를 CACHE_STATUS_START 변경처리한다.
// @param [IN] nMaxCout 추출 가능한 최대 갯수
// @param [OUT] 추출된 job 정보를 저장하기 위한 List
// @return false 추출 가능한 항목이 없을 경우.
// true 그 외
bool GetDataToStart( const unsigned int nMaxCount, std::list<CCacheData>& listData );
private:
// job 정보을 담당하는 map ( key-value 구조 )
// - key = content_length:uri 형식으로 구성하여 중복 제거 처리
std::map<std::string, CCacheData> m_mapJob;
// map 객체에 대한 접근제어를 담당할 mutex lock
pthread_mutex_t m_mutexMap;
private:
// 전달받은 정보를 바탕으로 map 에 사용할 Key 값을 생성한다.
// key 값은 동일 Contnt 중복 방지를 위해
// content_length:filename_hash(경로정보를 제외한 Only hash name) 형식으로 구성한다.
// @param data [IN] content length 및 filename hash 정보가 저장된 CCacheRequestData 객체 참조
// @param strKey [OUT] 생성된 key 값.
bool GetKey( CCacheRequestData & data, std::string& strKey );
};
#endif // __CACHE_JOB_LIST_H__
+421
View File
@@ -0,0 +1,421 @@
#include "CacheOrder.h"
#include <errno.h>
#include <string.h>
#include "Database.h"
#include "Logger.h"
// 처리 loop 간의 sleep time 정의
#define DEFAULT_SLEEP_TIME 3
// RCDB 접속 실패 또는 Query 실패 등의 상황 발생시
#define MAX_SLEEP_TIME 30
// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 2048
// 생성자
// param [IN] pQueueCacheRequest Cache 요청 정보 저장 Queue 에 대한 포인터
// param [IN] pListCacheJob Cache 생성 관련 Job List 객체에 대한 포인터
// param [IN] pProperFhs Proper FHS 정보를 저장하는 객체에 대한 포인터
CCacheOrder::CCacheOrder( CDataQueue<CCacheRequestData>* pQueueCacheRequest, CCacheJobList* pCacheJob, CProperFhs * pProperFhs )
: m_pQueueCacheRequest( pQueueCacheRequest )
, m_pCacheJob( pCacheJob )
, m_pProperFhs( pProperFhs )
{
m_threadHandle = 0;
m_uSleep = DEFAULT_SLEEP_TIME;
// NEW 2014-07-07 신규 멤버 변수 초기화 코드 추가.
// Init() 에서 RCDB 접속 실패가 발생 가능하므로.. 구형 RCDB 형상을 사용토록 설정.
m_bServiceTableSeparate = false;
}
// 소멸자
CCacheOrder::~CCacheOrder()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 객체 초기화 수행
bool CCacheOrder::Init()
{
// 생성자로 전달받은 각 객체에 대한 포인터는 유효하다가 판단하고....
// 각 멤버변수에 대한 검사는 수행하지 않는다.
// RCDB 접속 정보 Load.
m_rcdbInfo.Load();
// 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 )
{
LOG( LERR, "RCDB connection failed." );
return false;
}
else
{
// 접속 성공한 경우...
LOG( LINF, "RCDB connection test ok..." );
// NEW 2014-06-23 huibong
// 서비스 table 분리된 신규 형상인지.. 구형 형상인지 체크하여
// m_bServiceTableSeparate 변수 설정 처리.
// 해당 변수 초기화는 생성자에서 1차로 수행함.
char szQuery[1024];
snprintf( szQuery, 1023,
"SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource' " );
rcdb.PgDoExec( szQuery );
if( rcdb.PgResult( DataBase::NOT_CLEAR ) < 0 )
{
// Query 수행 결과 오류 발생시...
// - pg_tables 가 존재하지 않을 경우 오류 발생 가능...
// - 이는 RCDB 가 아직 적절하게 구성되지 않았다는 의미이므로... Init()함수를 오류로 처리한다.
LOG( LERR, "RCDB t_dav_resource exist check failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
else
{
// Query 수행이 정상인 경우.
int nResult = rcdb.GetNoTuples();
if( nResult > 0 )
{
// t_dav_resource 테이블이 존재하는 경우...
m_bServiceTableSeparate = false;
_LOG( LINF, "CCacheOrder: t_dav_resource table exist.[Old RCDB Type]" );
}
else
{
// t_dav_resource 테이블이 존재하지 않는 서비스별 테이블 분리 형상인 경우.
m_bServiceTableSeparate = true;
_LOG( LINF, "CCacheOrder: t_dav_resource table not exist.[New RCDB Type]" );
}
}
// RCDB 조회 결과 set 를 clear 처리 후 RCDB 접속 해제.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
}
// thread를 생성하여 업무 flow 를 시작.
bool CCacheOrder::Start()
{
int nRet = ::pthread_create( &m_threadHandle, 0, CCacheOrder::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// 스레드 함수
void* CCacheOrder::threadFunc( void* arg )
{
CCacheOrder* pObject = reinterpret_cast<CCacheOrder *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 1회 수행 후 지정된 시간 만큼 대기
sleep( pObject->m_uSleep );
}
// NEW 2014-07-07 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CCacheOrder::Execute()
{
// Request Cache Queue 상에 처리할 Data 가 존재하는 경우.
if( m_pQueueCacheRequest->GetSize() > 0 )
{
bool bResult = PushRequestToJob();
// 처리 결과에 따라 loop sleep time 조정
if( bResult == true )
m_uSleep = DEFAULT_SLEEP_TIME;
else
{
// RCDB 연결 실패, Queury 수행 실패 등의 오류 발생시
m_uSleep = MAX_SLEEP_TIME;
}
}
else
{
// 처리할 Data 가 존재하지 않는 경우.
m_uSleep = DEFAULT_SLEEP_TIME;
}
}
// Cache Requet Queue 에 저장된 요청을 검사하여....
// cache 생성 Job 에 추가하는 기능을 수행
bool CCacheOrder::PushRequestToJob()
{
// RCDB 연결 관련 객체.
// - RCDB 연결은 Job 에 등록가능한 Data 가 존재할 경우에만 수행되도록 한다.
DataBase rcdb;
bool bDbConnected = false; // RCDB 연결 상태 정보 저장 변수
// Queue 에서 Data 를 Pop 한 후
// 해당 정보가 CacheJobList 에 이미 등록된 정보인지 확인
// 미등록 정보인 경우 RCDB 조회를 수행하여
// - Cache 를 수행할 FHS 정보를 선택한 후
// - 해당 Content 정보를 Cache Job 에 등록 처리한다.
// queue 에서 꺼낸 정보를 저장하기 위한 변수
CCacheRequestData requestData;
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
// Query 수행 결과값 저장을 위한 변수..
int nResult = 0;
while( m_pQueueCacheRequest->GetSize() > 0 )
{
// queue 에 저장된 첫번째 data 복사 (queue 에서 삭제된 상태 아님)
requestData = m_pQueueCacheRequest->Front();
// 해당 정보를 Job List 에 저장 가능한지 확인
if( m_pCacheJob->IsExist( requestData ) == true )
{
// 이미 동일 정보가 Job List 에 존재하는 경우...
// queue 에서 pop 해서 버림.
_LOG( LINF, "Cache request already exist.[%s]", requestData.m_strFileNameHash.c_str() );
m_pQueueCacheRequest->Pop();
continue;
}
// RCDB 에서 해당 content 에 대한 정보 조회
// DB 연결이 되지 않은 상태인 경우.
// - Queue 항목 중 Job 에 등록해야 하는 항목이 존재할 경우에만 RCDB 연결 목적
if( bDbConnected == false )
{
// 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 )
{
// RCDB 접속이 안될때...
// - 현재 Queue 에 저장된 Cache Request 요청을 모두 버리고
// - false 반환하여 특정시간 동안 sleep 되도록 처리.
LOG( LERR, "RCDB connection failed. Cache request all trhow [%u]"
, m_pQueueCacheRequest->GetSize() );
// Queue 요청 모두 삭제 처리
m_pQueueCacheRequest->Init();
return false;
}
else
{
bDbConnected = true;
}
}
// Query 조립
// - 전달받은 filename_hash , content_length, FHS 정보를 이용하여
// - 해당 물리 파일과 연결된 uri 추출 ( uri 를 직접 수신할 경우..대소문자 무시 기능 때문에 조회 불가)
// - 추출된 uri 와 연결된 장비 정보를 조회한다.
// - ORDER BY 조건에 의해 is_cache 값이 N-R-Y 순으로 나타남.
// - COPY 등으로 인해 Query 결과에는 중복된 host_name 이 존재 가능함.
// t_dav_resource 테이블 사용 RCDB 인 경우
if( m_bServiceTableSeparate == false )
{
sprintf( szQuery,
"SELECT host_name, filename_hash, is_cache "
"FROM t_dav_resource "
"WHERE uri IN "
" ( SELECT uri FROM t_dav_resource "
" WHERE filename_hash = '%s' "
" AND host_name = '%s' "
" AND get_content_length = %llu "
" AND deleted_yn = 'N' "
" ) "
" AND get_content_length = %llu "
" AND deleted_yn = 'N' "
"ORDER BY is_cache ASC; "
, requestData.m_strFileNameHash.c_str()
, requestData.m_strRequestHost.c_str()
, requestData.m_uContentLength
, requestData.m_uContentLength
);
}
else
{
// 신형 RCDB 형상인 경우.
sprintf( szQuery,
"SELECT host_name, filename_hash, is_cache "
"FROM t_meta_%s "
"WHERE uri IN "
" ( SELECT uri FROM t_meta_%s "
" WHERE filename_hash = '%s' "
" AND host_name = '%s' "
" AND get_content_length = %llu "
" AND deleted_yn = 'N' "
" ) "
" AND get_content_length = %llu "
" AND deleted_yn = 'N' "
"ORDER BY is_cache ASC; "
, requestData.m_strServiceSeq.c_str()
, requestData.m_strServiceSeq.c_str()
, requestData.m_strFileNameHash.c_str()
, requestData.m_strRequestHost.c_str()
, requestData.m_uContentLength
, requestData.m_uContentLength
);
}
// Query 실행 및 결과 확인
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// RCDB t_dav_resource 테이블 조회 실패시
// - RCDB 연결 실패 또는 Query 실패로 인한 문제 발생 가능.
// - 따라서 RCDB 연결 해제 처리, Queue Pop 처리한 후
// - 루프를 계속 수행처리
LOG( LERR, "RCDB t_dav_resource cache select failed.[%s][%s]"
, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
bDbConnected = false;
m_pQueueCacheRequest->Pop();
continue;
}
// 결과 Row 수 확인.
nResult = rcdb.GetNoTuples();
if( nResult <= 0 )
{
// 해당 Content 가 존재하지 않거나... 오류 발생시.
// - 그냥 버린다.
// - 이로 인해 FHS 에 meta 기준으로 삭제 불가능한 파일이 남게 된다.
_LOG( LINF, "cache request content not found in RCDB.[%d] [%s][%s][%llu]"
, nResult
, requestData.m_strRequestHost.c_str()
, requestData.m_strFileNameHash.c_str()
, requestData.m_uContentLength );
}
else
{
// 결과 Row 수가 존재하는 경우...
CCacheData cacheData;
cacheData.m_requestData = requestData;
// 결과 정보 중.. Request 요청을 보낸 장비가 아닌 FHS 을 Cache 생성 원본으로 선택
// - order by 조건에 의해 is_cache 가 N-R-Y 순으로 장비 정보가 나타나므로...
// - 원본, 복제본, cache 본 순으로 사용될 순서가 정해짐.
for( int i = 0; i < nResult; ++i )
{
// host name 이 같지 않은 경우.
if( strcasecmp( rcdb.GetValue( i, 0 ), requestData.m_strRequestHost.c_str() ) != 0 )
{
cacheData.m_strSourceHost = rcdb.GetValue( i, 0 );
cacheData.m_strSourceFilenameHash = rcdb.GetValue( i, 1 );
// source 로 사용할 첫번째 장비를 찾은 경우...
// - 더 이상 루프는 의미가 없으므로..
break;
}
}
// 만약 원본 1개만 존재하는 경우...
// - 그냥 요청 정보를 그대로 사용.
if( cacheData.m_strSourceHost.empty() == true )
{
cacheData.m_strSourceHost = requestData.m_strRequestHost;
cacheData.m_strSourceFilenameHash = requestData.m_strFileNameHash;
}
// ignore FHS list 구성
// - 빠른 처리를 위해 조회 결과 처리시 중복 host_name 정보를 제거한다.
std::list<std::string> ignoreFhsList;
std::string strTemp;
for( int j = 0; j < nResult; ++j )
{
strTemp = rcdb.GetValue( j, 0 );
ignoreFhsList.push_back( strTemp );
}
// 중복 제거 ( sort() 후 unique() 함수 호출해야 중복 제거 가능 )
ignoreFhsList.sort();
ignoreFhsList.unique();
// Target FHS 선정 처리
std::vector<std::string> vecProperFhs;
if( m_pProperFhs->GetProperFhs( 1, vecProperFhs, ignoreFhsList ) == false )
{
// Proper FHS 선택 실패시...
// 해당 요청을 그냥 버린다...
_LOG( LWAR, "cache request not found proper fhs. throw.[%s][%s][%llu]"
, requestData.m_strRequestHost.c_str()
, requestData.m_strFileNameHash.c_str()
, requestData.m_uContentLength );
}
else
{
// Proper FHS 선택 성공시..
// - 해당 정보를 CacheData 에 입력한 후...
// - Job 으로 등록 처리
cacheData.m_strTargetHost = vecProperFhs.front();
m_pCacheJob->Register( cacheData );
}
}
// Queue Pop 처리 후
// 다음 Query 사용을 대비하여 결과 set clear 처리.
m_pQueueCacheRequest->Pop();
rcdb.PgClear();
}
// RCDB 연결이 되어 있는 경우.. 종료 처리
if( bDbConnected == true )
{
rcdb.PgClear();
rcdb.PgCloseDB();
}
return true;
}
+96
View File
@@ -0,0 +1,96 @@
/***************************************************************************
Cache 요청을 확인하여 Job 에 등록 처리를 수행
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __CACHE_ORDER_H__
#define __CACHE_ORDER_H__
#include <pthread.h>
#include "DataQueue.hpp"
#include "CacheRequestData.h"
#include "CacheJobList.h"
#include "ProperFhs.h"
#include "RcdbInfo.h"
// CCacheOrder
// FHS로부터 수신되어 Queue 에 저장된 Hot Content Cache 요청에 대해
// 중복 요청 여부 확인 및 RCDB 에서 해당 Content 를 저장한 FHS 를 확인한 후
// 실제 Cache 생성 작업이 진행되도록 Job List 에 저장하는 역활을 수행.
// 내부적으로 Thread를 사용
class CCacheOrder
{
public:
// 생성자
// param [IN] pQueueCacheRequest Cache 요청 정보 저장 Queue 에 대한 포인터
// param [IN] pCacheJob Cache 관련 Job List 객체에 대한 포인터
// param [IN] pProperFhs Proper FHS 정보를 저장하는 객체에 대한 포인터
CCacheOrder( CDataQueue<CCacheRequestData>* pQueueCacheRequest, CCacheJobList* pCacheJob, CProperFhs * pProperFhs );
// 소멸자
~CCacheOrder();
// 객체 초기화 수행
bool Init();
// thread를 생성하여 업무 flow 를 시작.
bool Start();
private:
// Cache 요청 정보 저장 Queue 에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CCacheRequestData>* m_pQueueCacheRequest;
// Cache 관련 Job List 객체에 대한 포인터
// - 생성자를 통해 전달받음
CCacheJobList* m_pCacheJob;
// Proper FHS 정보를 저장하는 객체에 대한 포인터
// - 생성자를 통해 전달받음
CProperFhs* m_pProperFhs;
// 내부 Thread 에 대한 ID
pthread_t m_threadHandle;
// RCDB 접속 정보를 저장할 변수.
CRcdbInfo m_rcdbInfo;
// New 2014-07-07 huibong
// RCDB 형상변경에 따라 서비스별 Table 분리 여부 판단 flag
// TODO: 향후 신규 RCDB 형상 적용 완료 후 본 항목 제거 필요.
// - false : 기존 t_dav_resource 테이블 사용.
// - ture : 신규 RCDB 형상 (서비스별 meta table 분리) 사용.
bool m_bServiceTableSeparate;
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
unsigned int m_uSleep;
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void Execute();
// Cache Requet Queue 에 저장된 요청을 검사하여....
// cache 생성 Job 에 추가하는 기능을 수행.
bool PushRequestToJob();
};
#endif // __CACHE_ORDER_H__
+98
View File
@@ -0,0 +1,98 @@
#include "CacheRequestData.h"
#include <stdlib.h>
#include "Logger.h"
CCacheRequestData::CCacheRequestData()
{
m_uContentLength = 0;
m_uConcurrentFd = 0;
}
// Set 함수
void CCacheRequestData::Set( struct stCacheRequestBody * pRequestBody )
{
m_strRcid = pRequestBody->szRCID;
m_strFileNameHash = pRequestBody->szFileNameHash;
m_strRequestHost = pRequestBody->szHostname;
m_strServiceSeq = pRequestBody->szServiceID;
m_uContentLength = atoll( pRequestBody->szContentLength );
m_uConcurrentFd = atoi( pRequestBody->szFDCount );
Trim( m_strRcid );
Trim( m_strFileNameHash );
Trim( m_strServiceSeq );
}
// 본 객체의 재사용을 위한 멤버 변수 초기화 함수
void CCacheRequestData::Init()
{
m_strRcid.clear();
m_strFileNameHash.clear();
m_strRequestHost.clear();
m_strServiceSeq.clear();
m_uContentLength = 0;
m_uConcurrentFd = 0;
}
// Key 값 생성 지원을 위해 수신된 filename_hash 정보 중
// 경로 정보를 제외한 hash name 만을 반환 처리한다.
// @param [OUT] value 추출된 hash name 정보 ( 경로정보 제외 )
// @return false hash name 정보가 유효화지 않은 경우.
// true 정상.
bool CCacheRequestData::GetOnlyHashFileName( std::string & value )
{
if( m_strFileNameHash.empty() == true )
return false;
std::string::size_type pos = m_strFileNameHash.rfind( '/' );
if( pos == std::string::npos )
{
// 잘못된 filename_hash 정보임.
return false;
}
// 경로 부분을 제외한 hash filename 부분만 추출.
value = m_strFileNameHash.substr( pos + 1 );
if( value.empty() == true )
return false;
return true;
}
void CCacheRequestData::Print()
{
_LOG( LDBG, "---- cache request data ----" );
_LOG( LDBG, "rc id [%s]", m_strRcid.c_str() );
_LOG( LDBG, "filename_hash [%s]", m_strFileNameHash.c_str() );
_LOG( LDBG, "host name [%s]", m_strRequestHost.c_str() );
_LOG( LDBG, "service id [%s]", m_strServiceSeq.c_str() );
_LOG( LDBG, "content length [%llu]", m_uContentLength );
_LOG( LDBG, "fd count [%u]", m_uConcurrentFd );
_LOG( LDBG, "-----------------------------" );
return;
}
// std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
void CCacheRequestData::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 );
}
+92
View File
@@ -0,0 +1,92 @@
/***************************************************************************
Cache 생성 요청 Data 정보 저장 Class
-----------------------------------------
begin : 2014/07/08
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.3
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 __CACHE_REQUEST_DATA_H__
#define __CACHE_REQUEST_DATA_H__
#include <string>
// fimngd 로 부터 수신된 Hot Content 정보 수신 관련 packet body 구조체.
// sizeof() : 1252 Byte (64Bit OS)
struct stCacheRequestBody {
char szRCID[32]; // RC ID 정보
char szFileNameHash[1024]; // FHS 에 저장된 filename_hash 정보
char szHostname[128]; // cache 발생 요청 장비의 Host Name
char szServiceID[32]; // service sequence ( 구 tran_id )
char szContentLength[32]; // content length ( 실제 물리 File size )
char szFDCount[4]; // 해당 content 의 참조 count.
};
// CCacheRequestData
// FHS 로 부터 수신된 Hot Content 정보 와
// Cache 생성을 위한 관련 정보를 저장하기 위한 data class.
class CCacheRequestData
{
public:
// 생성자
CCacheRequestData();
// 소멸자
~CCacheRequestData(){};
// Set 함수
void Set( struct stCacheRequestBody * pRequestBody );
// 본 객체의 재사용을 위한 멤버 변수 초기화 함수
void Init( void );
// Key 값 생성 지원을 위해 수신된 filename_hash 정보 중
// 경로 정보를 제외한 hash name 만을 반환 처리한다.
// @param [OUT] value 추출된 hash name 정보 ( 경로정보 제외 )
// @return false hash name 정보가 유효화지 않은 경우.
// true 정상.
bool GetOnlyHashFileName( std::string & value );
// Debug 목적 함수.
void Print( void );
public:
// RC ID 정보 ( FHS 수신 정보 )
std::string m_strRcid;
// filename_hash 정보( FHS 수신 정보 )
std::string m_strFileNameHash;
// Cache 생성을 요청한 Host 정보 ( FHS 수신 정보 )
std::string m_strRequestHost;
// Service sequece (구 tran_id) 정보 ( FHS 수신 정보 )
std::string m_strServiceSeq;
// Content length 정보 ( FHS 수신 정보 )
unsigned long long m_uContentLength;
// 동접 fd 갯수 ( FHS 수신 정보 )
unsigned int m_uConcurrentFd;
private:
// URI 항목 Trim 처리를 위한 함수
void Trim( std::string & str );
};
#endif // __CACHE_REQUEST_DATA_H__
+320
View File
@@ -0,0 +1,320 @@
#include "CacheThread.h"
#include <errno.h>
#include <time.h>
#include <string.h>
#include "Logger.h"
#include "FtsdSocketControl.h"
#include "ServiceConfig.h"
// ftsd 로 부터 처리 결과 수신을 위한 Total 대기 시간 정의 ( sec )
#define FHS_RESPONSE_TOTAL_TIMEOUT 3600 // ( sec, 1 hour )
// ftsd 로 부터 응답 수신 확인을 위한 socket read timeout (sec)
#define FHS_RESPONSE_CHECK_TIMEOUT 3 // 3 sec
CCacheThread::CCacheThread( CProcessStatus* pProcessStatus
, CCacheJobList* pJobList
, CDataQueue<CCacheData>* pQueueCacheComplete
, CCacheData& dataCache )
: m_pProcessStatus( pProcessStatus )
, m_pCacheJob( pJobList )
, m_pQueueCacheComplete( pQueueCacheComplete )
, m_dataCache( dataCache )
{
// 멤버 변수 초기화
m_threadHandle = 0;
}
CCacheThread::~CCacheThread()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// thread를 생성하여 Cache 생성 작업을 수행.
bool CCacheThread::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CCacheThread::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// 스레드 함수
void* CCacheThread::threadFunc( void* arg )
{
CCacheThread* pObject = reinterpret_cast<CCacheThread *>( arg );
pthread_detach( pthread_self() );
pthread_testcancel();
// Thread 동작시 Thread Count 1 증가 처리
pObject->m_pProcessStatus->AddReplicationCount();
// 실제 작업 수행.
pObject->Execute();
// Thread 동작 완료시 Thread Count 1 감소 처리
pObject->m_pProcessStatus->SubtractReplicationCount();
pthread_testcancel();
// NEW 2014-06-25 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
// 객체 자동 delete 처리.
delete pObject;
return NULL;
}
// 실제 Cache 생성 작업을 수행
void CCacheThread::Execute()
{
// 1. FHS 의 ftsd 와 통신 처리를 담당할 socket control 객체 생성.
CFtsdSocketControl fhs;
// 2. Source Content를 가진 FHS ftsd 에 접속 수행.
bool bResult = fhs.ConnectTarget( m_dataCache.m_strSourceHost, CServiceConfig::GetInstance()->GetFhsTransferDaemonPort() );
if( bResult == false )
{
// 원본 FHS 와 접속 실패시
// - 그냥 실패로 처리 ( Job 에서 본 항목 제거 처리 후 Thread 종료 )
_LOG( LERR, "CCacheThread: source fhs[%s] connect fail."
, m_dataCache.m_strSourceHost.c_str() );
m_pCacheJob->UnRegister( m_dataCache );
return;
}
// 3. 연결된 Source FHS 로 Cache 생성 요청을 전달
std::vector<std::string> vecTargetFHS;
vecTargetFHS.push_back( m_dataCache.m_strTargetHost );
bResult = fhs.SendFileCacheRequest( m_dataCache.m_strSourceFilenameHash
, m_dataCache.m_requestData.m_uContentLength
, CServiceConfig::GetInstance()->GetHashCheckLevel()
, true
, vecTargetFHS );
if( bResult == false )
{
// Cache 생성 요청 전달 실패시
// - 그냥 실패로 처리 ( Job 에서 본 항목 제거 처리 후 Thread 종료 )
_LOG( LERR, "CCacheThread: cache request send fail. [%s][%s]"
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str() );
m_pCacheJob->UnRegister( m_dataCache );
return;
}
// 4. FHS 로 부터 처리 결과를 수신할때 까지 대기
// - 이상 상황 발생시 무한 대기 가능성이 존재하므로..
// 미리 정의된 FHS_RESPONSE_TOTAL_TIMEOUT 까지만 대기하도록 한다.
// response timeout 처리를 위한 변수
time_t timeStart;
time_t timeNow;
// 응답 결과를 저장하기 위한 변수
int nResult;
bool bSuccess = false;
std::string szErrorMessage;
// 대기 시작 전 현재시간을 구한다.
timeStart = time( &timeStart );
// FHS 로 부터 응답 수신 대기
while( 1 )
{
// FHS 로 부터 응답 수신 처리
// -1 : 연결 종료
// 0 : Timeout 발생
// 1 : 응답 수신
// 2 : 복제 관련 응답이 아닌 다른 응답인 경우.. (발생하지 않지만.. 혹시나)
nResult = fhs.GetFileCacheResult( FHS_RESPONSE_CHECK_TIMEOUT
, bSuccess
, szErrorMessage
, m_mapSuccessFhs
, m_mapFailFhs );
if( nResult == 0 )
{
// 지정된 시간동안 응답 결과를 수신하지 못한 경우
// - FHS 와 연결이 정상인지 Alive 체크 호출
// - 만약 연결이 끝어진 경우.. 다음 loop 에서 -1 연결 종료가 리턴됨.
if( fhs.SendAliveCheck() == false )
{
_LOG( LERR, "CCacheThread: alive check fail.[%s][%s]"
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str() );
}
// Timeout 이 발생했으므로 바로 재시도하지 않고 조금 대기 후 다시 시도.
sleep( 1 );
}
else if( nResult == 1 )
{
// FHS 로 부터 응답을 수신한 경우.
// - Content 이상으로 인해 전송 자체를 하지 못했는지 여부 판단
if( bSuccess == true )
{
// 정상적으로 Cache 생성 처리된 경우..
_LOG( LDBG, "CCacheThread: result receive.[%s][%s]"
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str() );
DoSuccessProcess();
}
else
{
// Content 이상으로 인해 Cache 원본 Content 자체에 문제가 있는 경우.
// - 그냥 실패로 처리 ( Job 에서 본 항목 제거 처리 후 Thread 종료 )
_LOG( LERR, "CCacheThread: error result receive.[%s][%s][%s][%llu]"
, szErrorMessage.c_str()
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str()
, m_dataCache.m_requestData.m_uContentLength );
m_pCacheJob->UnRegister( m_dataCache );
}
// 처리 결과를 수신했으므로 loop 종료.
break;
}
else if( nResult == -1 )
{
// FHS 와 연결이 끝어진 경우.
// - 그냥 실패로 처리 ( Job 에서 본 항목 제거 처리 후 Thread 종료 )
_LOG( LERR, "CCacheThread: fhs connection broken.[%s][%s]"
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str() );
m_pCacheJob->UnRegister( m_dataCache );
break;
}
else if( nResult == 2 )
{
// Cache 관련 응답이 아닌 경우
// - 무시
;
}
else
{
// 기타 정의되지 않은 결과값 반환시
// - FHS 와 세션 이상이 발생할 수 있으므로
// - 그냥 실패로 처리 ( Job 에서 본 항목 제거 처리 후 Thread 종료 )
_LOG( LERR, "CCacheThread: receive undefine result.[%d][%s][%s]"
, nResult
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str() );
m_pCacheJob->UnRegister( m_dataCache );
break;
}
// Total Reponse 대기 시간 확인
if( difftime( time( &timeNow ), timeStart ) > FHS_RESPONSE_TOTAL_TIMEOUT )
{
// Total Response Timeout 발생시
// - 그냥 실패로 처리 ( Job 에서 본 항목 제거 처리 후 Thread 종료 )
_LOG( LERR, "CCacheThread: cache result receive timeout.[%s][%s]"
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str() );
m_pCacheJob->UnRegister( m_dataCache );
break;
}
} // while(1)
}
void CCacheThread::DoSuccessProcess()
{
// 멤버 변수에 저장된 정보를 처리하기 위한 iterator
std::map<std::string, std::string>::iterator itSuccessFhs;
// m_mapSuccessFhs 에 저장된 성공 결과에 대한 처리
// - success fhs host, hash_name 의 정보 저장된 상태
// - Complete Queue 에 저장.
for( itSuccessFhs = m_mapSuccessFhs.begin(); itSuccessFhs != m_mapSuccessFhs.end(); ++itSuccessFhs )
{
m_dataCache.m_strTargetHost = itSuccessFhs->first;
m_dataCache.m_strTargetFilenameHash = itSuccessFhs->second;
// complete queue 에 저장 처리
// - complete queue 가 full 상태일 수 있어 push 실패 가능성 존재
// - 하지만... complete queue 에 저장 못할 경우...
// FHS 에 생성된 Cache 는 처치 곤란한 쓰레기가 될 수 있으므로.... 최대한 push 처리
// - 만약 최종 push 실패시.. Job 에서 해당 항목 삭제 후 throw 처리한다.
int nRetry = 0;
while( m_pQueueCacheComplete->Push( m_dataCache ) == false )
{
++nRetry;
if( nRetry > 3 )
{
_LOG( LWAR, "Cache complete queue push fail. throw [%s][%s]"
, m_dataCache.m_strTargetHost.c_str()
, m_dataCache.m_strTargetFilenameHash.c_str() );
// 최종 Job 항목에서 삭제 처리한다.
m_pCacheJob->UnRegister( m_dataCache );
break;
}
else
{
// push 실패시 잠시 대기 후 재시도
sleep( 1 );
}
}
}
// m_mapFailFhs 에 저장된 실패 결과에 대한 처리
// - 로깅만 수행한다.
if( m_mapFailFhs.empty() == false )
{
// 멤버 변수에 저장된 정보를 처리하기 위한 iterator
// - failed fhs host, error message
std::map<std::string, std::string>::iterator itFailFhs;
// 실패 내역에 대한 로깅
for( itFailFhs = m_mapFailFhs.begin(); itFailFhs != m_mapFailFhs.end(); ++itFailFhs )
{
_LOG( LWAR, "CCacheThread: cache create fail [%s][%s]->[%s][%s]"
, m_dataCache.m_strSourceHost.c_str()
, m_dataCache.m_strSourceFilenameHash.c_str()
, itFailFhs->first.c_str()
, itFailFhs->second.c_str() );
}
// 최종 성공 항목은 존재하지 않고... 실패항목만 존재하는 경우..
// 실패로 판단하고.. Job 항목에서 제거 처리한다.
if( m_mapSuccessFhs.empty() == true )
{
// 최종 Job 항목에서 삭제 처리한다.
m_pCacheJob->UnRegister( m_dataCache );
}
}
return;
}
+90
View File
@@ -0,0 +1,90 @@
/***************************************************************************
Cache 생성 작업을 수행하는 Thread class
-----------------------------------------
begin : 2010/03/13
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __CACHE_THREAD_H__
#define __CACHE_THREAD_H__
#include <pthread.h>
#include <map>
#include <string>
#include "ProcessStatus.h"
#include "CacheData.h"
#include "CacheJobList.h"
#include "DataQueue.hpp"
// CCacheThread
// 전달받은 Content 에 대해 각 FHS 와 통신을 통해 실제 Cache 생성을 수행
// - Cache 정보 및 FHS 정보를 SocketControl 을 이용하여 ftsd 로 전송
// - 처리 결과 수신 후 CompletQueue 결과 저장처리
// 내부적으로 Thread를 사용
class CCacheThread
{
public:
// 생성자
CCacheThread( CProcessStatus* pProcessStatus
, CCacheJobList* pJobList
, CDataQueue<CCacheData>* pQueueCacheComplete
, CCacheData& dataCache );
~CCacheThread();
// thread를 생성하여 Cache 생성 작업을 수행.
bool Start();
private:
// 프로세스 상의 Queue, Thread 상태를 관리하는 CProcessStatus 객체에 대한 포인터
// - 생성자를 통해 전달받음
CProcessStatus * m_pProcessStatus;
// Cache 생성 관련 Job List 객체에 대한 포인터
// - 생성자를 통해 전달받음
CCacheJobList* m_pCacheJob;
// Cache 생성 완료 저장 Queue 에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CCacheData>* m_pQueueCacheComplete;
// Cache 생성 대상 content 정보
// - 생성자를 통해 전달받음
CCacheData m_dataCache;
// 내부 Thread 에 대한 ID
pthread_t m_threadHandle;
// ftsd 로 부터 정상 처리된 결과 정보를 수신하기 위한 멤버 객체
// - success fhs host, hash_name
std::map<std::string, std::string> m_mapSuccessFhs;
// ftsd 로 부터 실패 처리된 결과 정보를 수신하기 위한 멤버 객체
// - failed fhs host, error message
std::map<std::string, std::string> m_mapFailFhs;
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 실제 Cache 생성 작업을 수행
void Execute();
void DoSuccessProcess();
};
#endif //__CACHE_THREAD_H__
+170
View File
@@ -0,0 +1,170 @@
/****************************************************************************
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 는 기존 CQueue 클래스가 가지고 있는 문제점을 대체하기 위해
* 새로 개발된 class 임.
* 자세한 사항은 이슈 #19042 참고
*/
#ifndef __DATA_QUEUE_HPP__
#define __DATA_QUEUE_HPP__
#include <queue>
#include <pthread.h>
#define QUEUE_DEFAULT_MAX_SIZE 0
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 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__ */
+185
View File
@@ -0,0 +1,185 @@
/***************************************************************************
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 __DATABASE_H__
#define __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 // ~__DATABASE_H__
+188
View File
@@ -0,0 +1,188 @@
#include "FimngdSocketControl.h"
#include "Logger.h"
#include <arpa/inet.h>
// Packet Header stx 코드
#define HEADER_STX_CODE 0x02
// Packet Header type 구분코드
#define HEADER_TYPE_REQUEST 0x00
// Packet command : fimngd 요청 ( Command 3th Byte 만 사용 )
#define REQUEST_CACHE_CREATE 0x01
// 생성자.
// param [IN] clientSocket client 와 통신 연결된 socket.
CFimngdSocketControl::CFimngdSocketControl( int clientSocket )
: CBaseSocket( clientSocket )
, m_nPacketHeaderLen ( sizeof(m_packetHeader))
, m_nPacketDataLen( 0 )
{
}
// 소멸자.
CFimngdSocketControl::~CFimngdSocketControl()
{
// 소멸자 Socket 명시적 Close 처리.
Close();
}
// 수신된 Data 를 전달받은 인자에 설정하여 반환 처리
// @param [OUT] data 수신된 Data 를 저장할 객체에 대한 참조
// @param [in] timeout 대기 timeout (sec)
// @return -1 : Socket 오류 또는 잘못된 Data 수신시
// 0 : timeout 발생시
// 1 : 정상
int CFimngdSocketControl::GetData( CCacheRequestData & data, int timeout )
{
// Header 정보를 수신
int nResult = GetPacketHeader( timeout );
if( nResult == -1 )
{
// Socket 끊김 또는 정의되지 않은 Data 수신시.
return -1;
}
else if( nResult == 0 )
{
// timeout 발생시
return 0;
}
// Header Data 정상 수신시... command code 에 따른 처리.
if( m_packetHeader.command[2] == REQUEST_CACHE_CREATE )
{
// Cache 생성 요청인 경우...
// Body 부분이 고정 Packet 형식이므로....
// - 수신된 data_length 와 이를 저장할 구조체의 크기가 동일한지 확인
// - 만약 동일하지 않은 경우.. packet 오류로 처리
if( m_nPacketDataLen != sizeof( struct stCacheRequestBody ) )
{
LOG( LERR, "Cache request packet body size not valid.[%u]/[%lu]"
, m_nPacketDataLen, sizeof( struct stCacheRequestBody ) );
return -1;
}
// Data 부분 수신 처리.
if( GetBodyCacheRequest( data ) == false )
{
// body 수신 처리시 오류 발생
return -1;
}
return 1;
}
else
{
// 미정의된 command code 인 경우( 현재 1개 type 만 정의되어 있으므로. )
LOG( LERR, "Not defined command code." );
PrintHeaderToLog();
return -1;
}
}
// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void CFimngdSocketControl::PrintHeaderToLog()
{
_LOG( LINF, "------------------------------------" );
_LOG( LINF, "stx [%02x]", m_packetHeader.stx );
_LOG( LINF, "type [%02x]", m_packetHeader.type );
_LOG( LINF, "command [%02x][%02x][%02x][%02x]" , m_packetHeader.command[0], m_packetHeader.command[1] , m_packetHeader.command[2], m_packetHeader.command[3] );
_LOG( LINF, "result [%02x][%02x][%02x][%02x]" , m_packetHeader.result[0], m_packetHeader.result[1] , m_packetHeader.result[2], m_packetHeader.result[3] );
_LOG( LINF, "data_length [%u]", m_nPacketDataLen );
_LOG( LINF, "extend_code [%02x][%02x]", m_packetHeader.extend_code[0], m_packetHeader.extend_code[1] );
_LOG( LINF, "------------------------------------" );
}
// Packet Header 부분의 수신 처리를 위한 함수
// @param timeout [in] 대기시간.
// @retrun 0 : timeout
// 1 : 성공
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
int CFimngdSocketControl::GetPacketHeader( int timeout )
{
if( IsValidSocket() == false )
return -1;
// Socket 으로 부터 Packet Header 부분 수신.
// 수신된 정보는 멤버변수에 저장처리.
/// read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
int nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, timeout );
if( nRead == -2 )
{
// timeout 발생시
return 0;
}
else if( nRead <= 0 )
{
// timeout 을 제외한 그 밖의 오류 발생시
return -1;
}
else
{
// 정상 Data 수신시.
// STX code 검사.
if( m_packetHeader.stx != HEADER_STX_CODE )
{
LOG( LERR, "Not valid stx code." );
PrintHeaderToLog();
return -1;
}
// Type Code 검사
if( m_packetHeader.type != HEADER_TYPE_REQUEST )
{
LOG( LERR, "Not valid type code." );
PrintHeaderToLog();
return -1;
}
// Data Length 부분 값을 멤버 변수에 저장처리.
m_nPacketDataLen = ntohl( m_packetHeader.data_length );
return 1;
}
}
// FHS 의 Hot Content Cache 생성 요청 Packet Body 부분을 수신 처리
bool CFimngdSocketControl::GetBodyCacheRequest( CCacheRequestData & data )
{
// 수신된 Body 를 저장할 변수 생성.
struct stCacheRequestBody stBody;
// Body 부분 수신 처리.
int nRead = ReadNTimeout( &stBody, m_nPacketDataLen, 15 );
if( nRead == -2 )
{
// timeout 발생시
// - 지정된 시간 동안 Header 에 기록된 Body 크기를 읽지 못했으므로. 오류로 처리
LOG( LERR, "cache request body[%lu] read timeout.", sizeof( struct stCacheRequestBody ) );
return false;
}
else if( nRead <= 0 )
{
// timeout 을 제외한 그 밖의 오류 발생시
LOG( LERR, "cache request body read error[%d]", nRead );
return false;
}
else
{
// Body 부분 정상 수신시...
// - 수신된 Data 를 파싱하여....
// - 인자로 전달받은 data 객체에 저장 처리.
data.Set( &stBody );
return true;
}
}
+98
View File
@@ -0,0 +1,98 @@
/***************************************************************************
fimngd socket control class
-----------------------------------------
begin : 2014/07/04
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.3
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 __FIMNGD_SOCKET_CONTROL_H__
#define __FIMNGD_SOCKET_CONTROL_H__
#include "BaseSocket.h"
#include "CacheRequestData.h"
///< BYTE 타입 정의
#ifndef _BYTE_DEFINED
#define _BYTE_DEFINED
typedef unsigned char BYTE;
#endif // _BYTE_DEFINED
// fimngd 와의 통신 관련 Packet Header
// - 계산상 크기는 16Byte 이지만...
// - 통신상 sizeof() : 20Byte ( 64Bit OS )
struct stFimngdPacketHeader {
char stx; // Packet 유효성 관리 코드
char type; // Request or Response 여부 ( 0x00: Request, 0x01: Response )
char command[4]; // Command Code ( 4 Byte) : 3th 0x01 설정
char result[4]; // Result Code ( 4 Byte )
unsigned int data_length; // Packet Data 부분의 길이값 ( Network Byte Order 사용)
char extend_code[2]; // 확장 및 Padding bits ( 2 Byte )
};
// CFimngdSocketControl
// FHS 의 fimngd 로 부터 Hot Content 정보 수신 처리를 담당할 Socket 처리 Class
class CFimngdSocketControl : public CBaseSocket
{
public:
// 생성자.
// @param [IN] clientSocket client 와 통신 연결된 socket.
CFimngdSocketControl( int clientSocket );
// 소멸자.
~CFimngdSocketControl();
// 수신된 Data 를 전달받은 인자에 설정하여 반환 처리
// @param [OUT] data 수신된 Data 를 저장할 객체에 대한 참조
// @param [in] timeout 대기 timeout (sec)
// @return -1 : Socket 오류 또는 잘못된 Data 수신시
// 0 : timeout 발생시
// 1 : 정상
int GetData( CCacheRequestData & data, int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
private:
// Packet Header 변수
struct stFimngdPacketHeader m_packetHeader;
// m_packetHeader 구조체의 크기를 저장하기 위한 상수
const int m_nPacketHeaderLen;
// Packet Header 에 저장된 Data 부분의 길이 정보값.
unsigned int m_nPacketDataLen;
protected:
// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void PrintHeaderToLog( void );
// Packet Header 부분의 수신 처리를 위한 함수
// @param timeout [in] 대기시간.
// @retrun 0 : timeout
// 1 : 성공
// -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
int GetPacketHeader( int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT );
// FHS 의 Hot Content Cache 생성 요청 Packet Body 부분을 수신 처리
bool GetBodyCacheRequest( CCacheRequestData & data );
};
#endif /* __FIMNGD_SOCKET_CONTROL_H__ */
+64
View File
@@ -0,0 +1,64 @@
/***************************************************************************
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 처리 요청
// 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
+263
View File
@@ -0,0 +1,263 @@
/***************************************************************************
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 );
/// @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__ */
+510
View File
@@ -0,0 +1,510 @@
#include "Main.h"
#include "ProcessRename.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include "Worker.h"
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
// Accept 대기 Count 수
#define DEFAULT_ACCEPT_WAIT_COUNT 50
// 전역 객체 정보
// Hot Content 생성 관련 fimngd로 부터 정보 수신을 위한 TCP Listen Socket
int g_listenHotContentSocket = -1;
// 사용방법 표시
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 that control content replication, hot content cache flow.\n\n");
return;
}
// 버전 정보 표시
void PrintVersion()
{
fprintf(stderr, "\n");
fprintf(stderr, PROG_NAME " version: " PROG_VERSION "\n\n");
return;
}
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;
}
// conf 로딩 및 검사
std::string szErrorMessage;
if( CServiceConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false)
{
fprintf(stderr, "[ERR] %s\n\n", szErrorMessage.c_str());
return EXIT_FAILURE;
}
// Log 객체 생성 및 초기화
if( CLogger::Init( PROG_NAME, CServiceConfig::GetInstance()->GetLogPath(), LINF ) == false)
{
fprintf(stderr, "[ERR] Log module initialize failed.[%s]\n\n", CServiceConfig::GetInstance()->GetLogPath());
return EXIT_FAILURE;
}
// Daemonize...
// - nochdir: true(작업 디렉토리 변경 안함)
// - noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
if( daemon(1, 0) == -1)
{
fprintf(stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror(errno) );
ReadyToExit();
return EXIT_FAILURE;
}
// Signal 처리기 설정
SetSignalMain();
// Process 기동 관련 정보 로깅
std::vector<std::string> vecTemp;
std::vector< std::string >::const_iterator it;
_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", CServiceConfig::GetInstance()->GetLogPath(), PROG_NAME);
_LOG( LINF, "Log level : %d", CServiceConfig::GetInstance()->GetLogLevel());
_LOG( LINF, "RC ID : %s", CServiceConfig::GetInstance()->GetRcId());
_LOG( LINF, "RCDB : %s %u %s %s", CServiceConfig::GetInstance()->GetRcdbIp()
, CServiceConfig::GetInstance()->GetRcdbPort()
, CServiceConfig::GetInstance()->GetRcdbName()
, CServiceConfig::GetInstance()->GetRcdbAcct());
_LOG( LINF, "FHS deamon tcp port : %u", CServiceConfig::GetInstance()->GetFhsTransferDaemonPort());
_LOG( LINF, "Hash check level : %u", CServiceConfig::GetInstance()->GetHashCheckLevel());
_LOG( LINF, "Max queue size : %u", CServiceConfig::GetInstance()->GetMaxQueueSize() );
_LOG( LINF, "Replication");
_LOG( LINF, " max thread : %u", CServiceConfig::GetInstance()->GetMaxReplicationThread() );
_LOG( LINF, " max retry : %u", CServiceConfig::GetInstance()->GetMaxRetryCount());
_LOG( LINF, " select delay (sec) : %u", CServiceConfig::GetInstance()->GetTargetSelectInterval());
_LOG( LINF, " select on start (hour): %u", CServiceConfig::GetInstance()->GetTargetSelectIntervalOnStarting());
_LOG( LINF, " skip keyword" );
vecTemp = CServiceConfig::GetInstance()->GetSkipWord();
for( it = vecTemp.begin(); it != vecTemp.end(); it++ )
{
_LOG( LINF, " : %s", it->c_str());
}
_LOG( LINF, "Hot Content");
_LOG( LINF, " tcp listen port : %u", CServiceConfig::GetInstance()->GetFhsHotContentListenPort() );
_LOG( LINF, " minimum fd count : %u", CServiceConfig::GetInstance()->GetHotContentCreateMinimumFd() );
_LOG( LINF, " max thread : %u", CServiceConfig::GetInstance()->GetMaxCacheThread());
_LOG( LINF, " expire time(sec) : %u", CServiceConfig::GetInstance()->GetCacheFileTimeout());
_LOG( LINF, "***********************************************************");
// Log Level 재설정. -> conf 설정대로 변경 처리.
#ifdef _DEBUG_
CLogger::GetInstance()->SetLogLevel(LDBG);
#else
CLogger::GetInstance()->SetLogLevel(CServiceConfig::GetInstance()->GetLogLevel());
#endif
// NEW 2014-07-02 huibong
// fimngd 와의 통신을 UDP -> TCP 변경함에 따라
// Main Process 에서 이와 관련된 Socket 을 생성토록 기능 추가
if( MakeHotContentListenSocket( g_listenHotContentSocket ) == false )
{
LOG( LERR, "Hot content listen socket create failed. => process exit." );
return EXIT_FAILURE;
}
// Worker Process 생성
if( MakeProcessWorker( g_listenHotContentSocket ) == false )
{
LOG( LERR, "Worker process make failed. => process exit.");
ReadyToExit();
return EXIT_FAILURE;
}
// Process Rename
set_ps_display(PROG_NAME": Main [monitor worker process]", false);
// Main Process : 그냥 대기
while( 1 )
{
// Main Process 는 할 일이 없다.
// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
// => 따라서 그냥 시그널 대기
pause();
}
// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.
ReadyToExit();
return EXIT_SUCCESS;
}
// Hot Content 정보 수신을 위한 TCP Listen socket 을 생성한다.
bool MakeHotContentListenSocket( int & listenSocket )
{
listenSocket = socket( AF_INET, SOCK_STREAM, 0 );
if( listenSocket == -1 )
{
int errorNum = errno;
LOG( LERR, "Hot content listen socket create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
struct sockaddr_in listenSockAddr;
socklen_t listenSockLen = 0;
memset( &listenSockAddr, 0x00, sizeof( listenSockAddr ) );
listenSockAddr.sin_family = AF_INET;
listenSockAddr.sin_port = htons( CServiceConfig::GetInstance()->GetFhsHotContentListenPort() );
listenSockAddr.sin_addr.s_addr = htonl( INADDR_ANY );
listenSockLen = sizeof( listenSockAddr );
int result = 0;
// Socket Port Reuse Option Set
int opt = 1;
result = setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Hot content listen socket option[SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
// Keep Alive Option set
opt = 1;
result = setsockopt( listenSocket, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Hot content listen socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
// Socket Bind
result = bind( listenSocket, ( struct sockaddr * )&listenSockAddr, listenSockLen );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Hot content listen socket bind failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
// Socket Listen
result = listen( listenSocket, DEFAULT_ACCEPT_WAIT_COUNT );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Hot content listen socket listen failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
/// @brief Worker Process 생성(fork) 처리 함수
/// @return 생성 성공시 true, 실패시에는 false 를 반환.
bool MakeProcessWorker( int listenHotContentSocket )
{
// Worker 프로세스 fork
pid_t processId = fork();
if( processId < 0 ) // fork fail
{
int errorNum = errno;
LOG( LERR, "Worker process create failed. [%d][%s]", errorNum, strerror(errorNum) );
return false;
}
else if( processId == 0 ) // Child Process => Worker Process
{
// Worker Process Main 함수 호출 및 종료처리.
int iResult;
iResult = WorkerMain( listenHotContentSocket );
ReadyToExit();
// WorkerMain() 함수 return 값으로 종료처리되도록 한다.
exit( iResult );
}
else
{
// Parent Process => Logging
_LOG(LINF, "Worker[%d] create success." , processId);
// 잠시 대기
// CHG 2015-09-14 huibong
// - #24369 usleep -> nanosleep 으로 변경 처리.
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 200000000; // 0.2 sec
nanosleep( &sleep, NULL );
}
return true;
}
/// @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);
}
/// @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 )
{
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
fprintf(stderr, "[ERR] Process duplication check failed.[popen error][%s]\n", strerror(errno));
return true;
}
else
{
memset( tempBuffer, 0x00, sizeof(tempBuffer) );
while( fgets( tempBuffer, sizeof(tempBuffer)-1, fd) != NULL )
{
string tempPid( tempBuffer );
Trim(tempPid);
if( atoi( tempPid.c_str() ) != getpid() )
{
fprintf(stderr, "[info] Process duplication found. pid[%d]\n", atoi(tempPid.c_str()));
bRun = true;
break;
}
}
pclose( fd );
return bRun;
}
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExit()
{
if( g_listenHotContentSocket != -1 )
{
close( g_listenHotContentSocket );
g_listenHotContentSocket = -1;
}
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
/// @brief Main Process 가 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
/// @param nSignalNumber [in] 발생한 시그널 Number
/// @return void
static void SignalMainTerminate(int nSignalNumber)
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "Main[%d] process exit by user signal[SIGTERM]. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "Main[%d] process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber );
}
ReadyToExit();
// CHG 2015-09-14 huibong
// - #24369 usleep -> nanosleep 으로 변경 처리.
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
/// @brief Main Process 에서 자식 프로세스인 Worker Process 가 종료된 경우 시그널 처리를 위한 함수
/// @param nSignalNumber [in] 발생한 시그널 Number
/// @return void
static void SignalWorkerDead(int nSignalNumber)
{
pid_t deadPid;
int nDeadStatus;
while( (deadPid = waitpid(-1, &nDeadStatus, WNOHANG)) > 0 )
{
// Worker 프로세스가 Signal 에 의해 종료되었는가?
if( WIFSIGNALED( nDeadStatus ) )
{
_LOG( LWAR, "Worker[%d] killed by signal[%d].", deadPid, WTERMSIG(nDeadStatus));
}
else
{
_LOG( LWAR, "Worker[%d] killed. Not signal.", deadPid);
// Worker 프로세스가 EXIT_FAILURE 반환 ( 초기화 실패시 )
// 해당 내역을 화면 및 로그 상에 출력하고
// Worker 프로세스를 재생성 처리하지 않는다.
if( WIFEXITED( nDeadStatus ) )
{
if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
{
_LOG( LERR, "Worker[%d] initilaize failed.", deadPid);
_LOG( LERR, "Main[%d] abnormally exit by worker initialize failed. Good Bye..", getpid());
ReadyToExit();
// CHG 2015-09-14 huibong
// - #24369 usleep -> nanosleep 으로 변경 처리.
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
}
}
// Worker Process 재생성 처리.
if( MakeProcessWorker( g_listenHotContentSocket ) == false )
{
// Worker Process 재성성 실패시
// 로깅 처리 후 Main 프로세스 종료 시그널 발생
_LOG( LERR, "Worker process recreate failed. => Main process exit.");
raise( SIGTERM );
}
else
{
_LOG( LWAR, "Worker process recreate success.");
}
}
// 오류 발생시 해당 내역 로깅
if( deadPid < 0 )
{
int errorNum = errno;
LOG( LERR, "Main process error: SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror(errorNum));
}
return;
}
/// @brief Main 프로세스 signal 처리 설정을 위한 함수
/// @return void
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 신호 처리 => ? */
// Child Process 인 Worker 프로세스 종료에 대한 처리기 설정.
act.sa_handler = SignalWorkerDead;
sigaction( SIGCHLD, &act, NULL);
// 사용자 종료 또는 에러 관련 신호 처리.
act.sa_handler = SignalMainTerminate;
sigaction( SIGTERM, &act, NULL); /* kill -TERM 에 의한 프로세스 종료시 */
sigemptyset(&set); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask(SIG_SETMASK, &set, NULL);
}
+64
View File
@@ -0,0 +1,64 @@
/***************************************************************************
rc_rmcd Main Header ( Main.h )
-----------------------------------------
begin : 2010/03/09
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.3
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_RMCD_MAIN_H__
#define __RC_RMCD_MAIN_H__
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
// 사용 방법을 화면에 표시하기 위한 함수.
void PrintUsage(void);
// Version 정보를 화면에 표시하기 위한 함수
void PrintVersion(void);
// Hot Content 정보 수신을 위한 TCP Listen socket 을 생성한다.
bool MakeHotContentListenSocket( int & listenSocket );
/// @brief Worker Process 생성(fork) 처리 함수
/// @return 생성 성공시 true, 실패시에는 false 를 반환.
bool MakeProcessWorker( int listenHotContentSocket );
// std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
// @param str [in/out] Trim 할 문자열을 저장한 String 참조변수.
// @return void
void Trim( std::string & str );
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
bool IsCurrentProcessRun( void );
// 프로세스 종료에 따른 반복적인 종료 관련 작업을 수행하기 위한 함수.
void ReadyToExit(void);
/// @brief Main 프로세스 signal 처리 설정을 위한 함수
/// @return void
void SetSignalMain( void );
/* Signal 처리를 위한 각 Signal Handler 함수는
* 다른 Code 에서 Include 처리시 Static 관련 문제로 인해
* 본 Header 에서 선언처리 하지 않음. cpp 에만 존재
*/
#ifdef __cplusplus
}
#endif
#endif /* __RC_RMCD_MAIN_H__ */
+83
View File
@@ -0,0 +1,83 @@
#****************************************************************************
# Makefile for rc_rmcd
# -----------------------------------------
#
# begin : 2013/05/19
# 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.
#*****************************************************************************
# Program info
PROG_NAME = rc_rmcd
REVISION = 1428
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
DEFAULT_CONFIG_FILE = /user/service/etc/rcts.conf
INSTALL_BIN = /user/service/bin
INSTALL_CONF = /user/service/etc
# Compiler info
CC = /usr/bin/g++
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized\
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
-fno-rtti -D_REENTRANT
LFLAGS =
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"
# Application Enviroment
APP = $(PROG_NAME)
DIR_INCLUDE = -I./. -I../lib -I /user/db/pgsql/include -I../
DIR_LIB = -L../lib
OBJ = ServiceConfig.o RcdbInfo.o Util.o Database.o \
CacheRequestData.o CacheData.o CacheJobList.o \
ProcessStatus.o ProperFhs.o \
ReplicateInsert.o ReplicateSelect.o ReplicateQueueControl.o \
FtsdSocketControl.o ReplicateThread.o ReplicateControl.o \
FimngdSocketControl.o CacheDelete.o CacheInsert.o CacheOrder.o \
CacheThread.o CacheControl.o\
Worker.o Main.o
LIBS = ../lib/libInterCommon.a -lpthread -lcrypt /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.$(PROG_NAME) *.out *.log
-rm -f $(APP)
sync
install : $(APP)
-cp $(APP) $(INSTALL_BIN)/$(APP)
sync
# End of Makefile
+297
View File
@@ -0,0 +1,297 @@
#include "ProcessStatus.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "Logger.h"
#include "ProcessRename.h"
// NEW 2014-06-19 huibong
// 설정 가능한 동시 Thread 수를 제한.
#define MAX_THREAD_COUNT_LIMIT 1024
// Process 정보 update 주기 (sec)
#define PROCESS_STATUS_UPDATE_TIME 2
////////////////////////////////////////
// 생성자
CProcessStatus::CProcessStatus( CDataQueue<CSourceData>* const pQueueSource
, CDataQueue<CRetryData>* const pQueueRetry
, CDataQueue<CCompleteData>* const pQueueCompleteReplication
, CDataQueue<CCacheRequestData>* const pQueueCacheRequest
, CDataQueue<CCacheData>* const pQueueCacheComplete
, CCacheJobList* const pCacheJob )
: m_pQueueSource( pQueueSource )
, m_pQueueRetry( pQueueRetry )
, m_pQueueCompleteReplication( pQueueCompleteReplication )
, m_pQueueCacheRequest( pQueueCacheRequest )
, m_pQueueCacheComplete( pQueueCacheComplete )
, m_pCacheJob( pCacheJob )
{
m_threadHandle = 0;
m_nMaxReplicationThreadCount = 0;
m_nMaxCacheThreadCount = 0;
m_nCurrentReplicationCount = 0;
m_nCurrentCacheCount = 0;
// Lock 초기화
pthread_mutex_init(&m_mxReplicateThreadCount, NULL);
pthread_mutex_init(&m_mxCacheThreadCount, NULL);
}
// 소멸자
CProcessStatus::~CProcessStatus()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
pthread_mutex_destroy(&m_mxReplicateThreadCount);
pthread_mutex_destroy(&m_mxCacheThreadCount);
}
// 객체 초기화
// @param [in] nMaxReplicationThreadCount Replication 처리 관련 동시 최대 생성 가능 Thread 수
// @param [in] nMaxCacheThreadCount Hot Content cache 처리 관련 동시 최대 생성 가능 Thread 수
// @return true 성공
// false 생성자로 전달된 객체 포인터가 유효하지 않은 경우,
// 인자로 전달된 최대 Thread 수가 너무 큰 경우.(MAX_THREAD_COUNT_LIMIT 초과시)
bool CProcessStatus::Init(const unsigned int nMaxReplicationThreadCount, const unsigned int nMaxCacheThreadCount)
{
// 생성자로 전달된 변수 Check.
if( m_pQueueSource == NULL || m_pQueueRetry == NULL || m_pQueueCompleteReplication == NULL
|| m_pQueueCacheRequest == NULL || m_pQueueCacheComplete == NULL || m_pCacheJob == NULL )
{
LOG( LERR, "module constructor parameter include null. check parameter" );
return false;
}
// 전달받은 동시 생성가능 Thread 가 내부 정의된 제약값을 초과하는지 확인
if( nMaxReplicationThreadCount > MAX_THREAD_COUNT_LIMIT )
{
LOG( LERR, "replication max thread count[%u] is too high. set below limit[%d]"
, nMaxReplicationThreadCount, MAX_THREAD_COUNT_LIMIT );
return false;
}
if( nMaxCacheThreadCount > MAX_THREAD_COUNT_LIMIT )
{
LOG( LERR, "hot content cache max thread count[%u] is too high. set below limit[%d]"
, nMaxCacheThreadCount, MAX_THREAD_COUNT_LIMIT );
return false;
}
m_nMaxReplicationThreadCount = nMaxReplicationThreadCount;
m_nMaxCacheThreadCount = nMaxCacheThreadCount;
// CHG 2014-06-18 huibong
// - 본 함수는 Worker 상에서 각 객체 모듈 초기화 중에 호출되므로...
// 프로세스 표시명을 아직 변경해서는 안된다.
//UpdateTitle();
return true;
}
// thread를 생성하여 관련 작업을 수행.
bool CProcessStatus::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CProcessStatus::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
void* CProcessStatus::threadFunc( void* arg )
{
CProcessStatus* pObject = reinterpret_cast<CProcessStatus *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->UpdateTitle();
pthread_testcancel();
sleep( PROCESS_STATUS_UPDATE_TIME );
}
// NEW 2014 - 06 - 23 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// 현재 프로세스 표시 정보 업데이트
void CProcessStatus::UpdateTitle(void)
{
// Rs: replication source queue size
// Rt: current replication thread count
// Rr: replication retry queue size
// Rc: replication complete queue size
// Cr: FHS 로 부터 수신된 Hot Content Cache 요청 정보 저장 queue size
// Cj: hot content cache job list size
// Ct: current hot content cache thread count
// Cc: hot content cache complete queue size
#if defined(__FreeBSD__)
setproctitle("[Rs%u Rt%d Rr%u Rc%u Cr%u Ct%d Cj%u Cc%u]",
m_pQueueSource->GetSize(),
m_nCurrentReplicationCount,
m_pQueueRetry->GetSize(),
m_pQueueCompleteReplication->GetSize(),
m_pQueueContentInfo->GetSize(),
m_nCurrentCacheCount,
m_pListJob->GetSize(),
m_pQueueCompleteCache->GetSize() );
#else
char szTemp[100] = {0};
sprintf(szTemp, "%s: [Rs%u Rt%d Rr%u Rc%u Cr%u Cj%d Ct%d Cc%u]",
PROG_NAME,
m_pQueueSource->GetSize(),
m_nCurrentReplicationCount,
m_pQueueRetry->GetSize(),
m_pQueueCompleteReplication->GetSize(),
m_pQueueCacheRequest->GetSize(),
m_pCacheJob->GetSize(),
m_nCurrentCacheCount,
m_pQueueCacheComplete->GetSize() );
set_ps_display(szTemp, false);
#endif
return ;
}
// Replication 관련 동시 생성 가능한 Thread 수를 반환
unsigned int CProcessStatus::GetValidReplicationCount(void)
{
// 생성 불가인 경우.
if( m_nMaxReplicationThreadCount == 0 )
return 0;
else
{
int nAvailCount;
// lock 설정 후 사용 가능 Thread 수 계산.
pthread_mutex_lock( &m_mxReplicateThreadCount );
nAvailCount = m_nMaxReplicationThreadCount - m_nCurrentReplicationCount;
pthread_mutex_unlock( &m_mxReplicateThreadCount );
// 현재 생성된 Thread 수가 Max 값보다 큰 경우
// 사용 가능 Thread 수를 0 으로 전달.
if( nAvailCount < 0 )
return 0;
else
return nAvailCount;
}
}
// Hot Content Cache 관련 동시 생성 가능한 Thread 수를 반환
unsigned int CProcessStatus::GetValidCacheCount(void)
{
// 생성 불가인 경우.
if( m_nMaxCacheThreadCount <= 0 )
return 0;
else
{
int nAvailCount;
// lock 설정 후 사용 가능 Thread 수 계산.
pthread_mutex_lock( &m_mxCacheThreadCount );
nAvailCount = m_nMaxCacheThreadCount - m_nCurrentCacheCount;
pthread_mutex_unlock( &m_mxCacheThreadCount );
// 현재 생성된 Thread 수가 Max 값보다 큰 경우
// 사용 가능 Thread 수를 0 으로 전달.
if( nAvailCount < 0 )
return 0;
else
return nAvailCount;
}
}
// Replication 관련 작업 Thread 수를 1 증가 처리
void CProcessStatus::AddReplicationCount(void)
{
AddCount(Replication);
}
// Replication 관련 작업 Thread 수를 1 감소 처리
void CProcessStatus::SubtractReplicationCount(void)
{
SubtractCount(Replication);
}
// Hot content cache 관련 작업 Thread 수를 1 증가 처리
void CProcessStatus::AddCacheCount(void)
{
AddCount(Cache);
}
// Hot content cache 관련 작업 Thread 수를 1 감소 처리
void CProcessStatus::SubtractCacheCount(void)
{
SubtractCount(Cache);
}
// 전달받은 작업의 Thread 수를 1 증가 시킨다.
void CProcessStatus::AddCount(Job job)
{
if( job == Cache )
{
pthread_mutex_lock(&m_mxCacheThreadCount);
++m_nCurrentCacheCount;
pthread_mutex_unlock(&m_mxCacheThreadCount);
}
else if( job == Replication )
{
pthread_mutex_lock(&m_mxReplicateThreadCount);
++m_nCurrentReplicationCount;
pthread_mutex_unlock(&m_mxReplicateThreadCount);
}
// 프로세스 출력 표시명 갱신
// - Worker 프로세스에서 주기적으로 하므로.. 제거 처리.
//UpdateTitle();
}
// 전달받은 작업의 Thread 수를 1 감소 시킨다.
void CProcessStatus::SubtractCount(Job job)
{
if( job == Cache )
{
pthread_mutex_lock(&m_mxCacheThreadCount);
--m_nCurrentCacheCount;
pthread_mutex_unlock(&m_mxCacheThreadCount);
}
else if( job == Replication )
{
pthread_mutex_lock(&m_mxReplicateThreadCount);
--m_nCurrentReplicationCount;
pthread_mutex_unlock(&m_mxReplicateThreadCount);
}
// 프로세스 출력 표시명 갱신
// - Worker 프로세스에서 주기적으로 하므로.. 제거 처리.
//UpdateTitle();
}
+156
View File
@@ -0,0 +1,156 @@
/***************************************************************************
Process 상태 정보 저장 및 console 출력 처리 class
-----------------------------------------
begin : 2010/03/05
copyright : (C) 2005 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
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_STATUS_H__
#define __PROCESS_STATUS_H__
#include <pthread.h>
#include "DataQueue.hpp"
#include "ReplicationData.h"
#include "CacheRequestData.h"
#include "CacheJobList.h"
// CProcessStatus class
// - 프로세스 내부에서 사용 중인 Queue 정보와
// - Replcation 처리 thread 수, Hot Content 처리 thread 수를 관리,
// - 해당 정보를 console 상에 표시하는 역활을 수행한다.
// - 2016-06-18 다른 모듈과 연동시 반환값 처리 관련 문제가 많아
// 확장성 포기하고 업무 Flow 에 맞게 단순하게 동작하도록 수정 처리함.
// 특히 무제한 설정 관련 기능은 전부 제거 처리함.
class CProcessStatus
{
private:
// Replication 처리 관련 동시 최대 생성 가능 Thread 수.
int m_nMaxReplicationThreadCount;
// Hot Content cache 처리 관련 동시 최대 생성 가능 Thread 수
int m_nMaxCacheThreadCount;
// Lock 처리 관련
pthread_mutex_t m_mxReplicateThreadCount;
pthread_mutex_t m_mxCacheThreadCount;
////// 프로세스 title 로 모니터 대상 정보 /////////////
// Replication 처리 관련 현재 생성된 Thread 수.
int m_nCurrentReplicationCount;
// Hot Content cache 처리 관련 현재 생성된 Thread 수
int m_nCurrentCacheCount;
// Replication content 저장 queue 에 대한 포인터
// 생성자를 통해 전달받음.
CDataQueue<CSourceData>* m_pQueueSource;
// Replication 재시도 content 저장 queue 에 대한 포인터
// 생성자를 통해 전달받음.
CDataQueue<CRetryData>* m_pQueueRetry;
// Replication 완료 정보 저장 queue 에 대한 포인터
// 생성자를 통해 전달받음.
CDataQueue<CCompleteData>* m_pQueueCompleteReplication;
// Hot Content cache 요청 정보 저장 관련 queue 에 대한 포인터.
CDataQueue<CCacheRequestData>* m_pQueueCacheRequest;
// Hot Content cache 처리 완료 관련 queue 에 대한 포인터.
CDataQueue<CCacheData>* m_pQueueCacheComplete;
// Hot Content cache 생성 관리 job 객체에 대한 포인터.
CCacheJobList* m_pCacheJob;
//////////////////////////////////////////////////
// 작업의 종류를 구분하는 열거형 변수
// Replication : 0, Cache : 1
enum Job { Replication, Cache };
// 쓰레드 핸들
pthread_t m_threadHandle;
public :
// 생성자
// @param [in] pQueueSource : Replication content 저장 queue 에 대한 포인터
// @param [in] pQueueRetry : Replication 재시도 content 저장 queue 에 대한 포인터
// @param [in] pQueueCompleteReplication : Replication 완료 정보 저장 queue 에 대한 포인터
// @param [in] pQueueContentInfo : 캐시 요청 정보 저장 관련 queue 에 대한 포인터.
// @param [in] pQueueCompleteCache : 캐시 처리 완료된 queue 에 대한 포인터.
// @param [in] pListJob : 캐시 생성 job 객체에 대한 포인터.
CProcessStatus( CDataQueue<CSourceData>* const pQueueSource
, CDataQueue<CRetryData>* const pQueueRetry
, CDataQueue<CCompleteData>* const pQueueCompleteReplication
, CDataQueue<CCacheRequestData>* const pQueueCacheRequest
, CDataQueue<CCacheData>* const pQueueCacheComplete
, CCacheJobList* const pCacheJob
);
// 소멸자
~CProcessStatus();
// 객체 초기화
// @param [in] nMaxReplicationThreadCount Replication 처리 관련 동시 최대 생성 가능 Thread 수
// @param [in] nMaxCacheThreadCount Hot Content cache 처리 관련 동시 최대 생성 가능 Thread 수
// @return true 성공
// false 생성자로 전달된 객체 포인터가 유효하지 않은 경우,
// 인자로 전달된 최대 Thread 수가 너무 큰 경우.(MAX_THREAD_COUNT_LIMIT 초과시)
bool Init(const unsigned int nMaxReplicationThreadCount, const unsigned int nMaxCacheThreadCount);
// thread를 생성하여 프로세스 출력 상태 갱신 작업을 수행.
bool Start();
// 현재 프로세스 표시 정보 업데이트
void UpdateTitle( void );
// Replication 관련 동시 생성 가능한 Thread 수를 반환
unsigned int GetValidReplicationCount( void );
// Hot Content Cache 관련 동시 생성 가능한 Thread 수를 반환
unsigned int GetValidCacheCount( void );
// Replication 관련 작업 Thread 수를 1 증가 처리
void AddReplicationCount(void);
// Replication 관련 작업 Thread 수를 1 감소 처리
void SubtractReplicationCount(void);
// Hot content cache 관련 작업 Thread 수를 1 증가 처리
void AddCacheCount(void);
// Hot content cache 관련 작업 Thread 수를 1 감소 처리
void SubtractCacheCount(void);
private :
// 스레드 함수
static void* threadFunc( void* arg );
// 전달받은 작업의 Thread 수를 1 증가 시킨다.
void AddCount(CProcessStatus::Job job);
// 전달받은 작업의 Thread 수를 1 감소 시킨다.
void SubtractCount(CProcessStatus::Job job);
};
#endif //__PROCESS_STATUS_H__
+322
View File
@@ -0,0 +1,322 @@
#include "ProperFhs.h"
#include <errno.h>
#include <string.h>
#include <algorithm>
#include "Logger.h"
#include "Database.h"
// 처리 loop 간의 sleep time 정의
#define DEFAULT_SLEEP_TIME 30
// RCDB 접속 실패 또는 Query 실패 등의 상황 발생시
#define MAX_SLEEP_TIME 180
// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 512
// 생성자
CProperFhs::CProperFhs()
{
// 멤버 변수 초기화
pthread_mutex_init( &m_mutexLock, NULL );
m_indexRoundRobin = 0;
m_threadHandle = 0;
m_uSleep = DEFAULT_SLEEP_TIME;
}
// 소멸자
CProperFhs::~CProperFhs()
{
pthread_mutex_destroy( &m_mutexLock );
if(m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 객체 초기화
bool CProperFhs::Init( )
{
// RCDB 접속 정보 Load.
m_rcdbInfo.Load();
// 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 )
{
LOG( LERR, "RCDB connection failed." );
return false;
}
else
{
// 접속 성공한 경우...
LOG( LINF, "RCDB connection test ok..." );
rcdb.PgCloseDB();
return true;
}
}
// thread를 생성하여 업무 flow 를 시작.
bool CProperFhs::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CProperFhs::threadFunc, this );
if( nRet )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// 스레드 함수
void* CProperFhs::threadFunc( void* arg )
{
CProperFhs* pObject = reinterpret_cast<CProperFhs *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 1회 수행 후 지정된 시간 만큼 대기
sleep( pObject->m_uSleep );
}
// NEW 2014-06-19 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
bool CProperFhs::Execute()
{
if( UpdateFromDB() == false )
{
// RCDB 연결 또는 query 관련 문제 발생시...
// sleep time 을 높인다.
m_uSleep = MAX_SLEEP_TIME;
}
else
{
m_uSleep = DEFAULT_SLEEP_TIME;
}
return true;
}
// Proper fhs의 목록을 RCDB t_fhs_top_status 에서 조회.
// CHG 2014-06-24 huibong
// #19042 의 팀회의 결과에 따라
// - RCDB 연결 실패 및 Query 오류 발생시 이전 FHS 목록 유지
// - RCDB 에서 조회된 Proper FHS 가 없는 경우.. Proper FHS 목록 Clear 처리
// - 그외 정상 처리.
bool CProperFhs::UpdateFromDB()
{
// 1. RCDB 와 연결처리.
DataBase 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 )
{
// RCDB 접속이 실패
// - 기존 proper fhs 항목 유지 처리
LOG( LERR, "RCDB connection failed." );
return false;
}
// 2. RCDB t_fhs_top_status 테이블 조회
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
// Query 수행 결과값 저장을 위한 변수..
int nResult = 0;
// t_fhs_top_status 테이블에 업데이트 된 FHS 목록 중...
// 최근 1분내에 갱신된 정보만 사용한다.
// - 신형 rc_statd 에서는 29 sec 마다 갱신 처리
snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
"SELECT fhs_hostname FROM t_fhs_top_status "
" WHERE lastupdate >= (NOW() - interval'01 minute') " );
// Query 수행 및 결과 확인
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// Query 수행 실패시
// - RCDB 연결 오류 발생의 경우.
// - 기존 proper fhs 항목 유지 처리
LOG( LERR, "RCDB t_fhs_top_status select failed.[%s][%s]"
, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
// 결과 Row 수 확인
nResult = rcdb.GetNoTuples();
if( nResult < 0 )
{
// 오류 발생시
// - 기존 proper fhs 항목 유지 처리
LOG( LERR, "RCDB t_fhs_top_status GetNoTuples() error[%s][%s]"
, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
else if( nResult == 0 )
{
// 조회 결과가 존재하지 않는 경우
// - proper fhs 목록 초기화.
_LOG( LWAR, "CProperFhs: Proper fhs not exist. Check RCDB t_fhs_top_status table.");
rcdb.PgClear();
rcdb.PgCloseDB();
pthread_mutex_lock( &m_mutexLock );
m_vecFHS.clear();
m_indexRoundRobin = 0;
pthread_mutex_unlock( &m_mutexLock );
return true;
}
// 3. 조회 결과가 존재하는 경우.
// - 내부 변수에 저장
pthread_mutex_lock( &m_mutexLock );
m_vecFHS.clear();
m_indexRoundRobin = 0;
std::string strTemp;
for( int i = 0; i < nResult; i++ )
{
strTemp = rcdb.GetValue( i, 0 );
if( strTemp.size() > 0 )
{
_LOG( LDBG, "CProperFhs: add host [%s]", strTemp.c_str() );
m_vecFHS.push_back( strTemp );
}
}
pthread_mutex_unlock( &m_mutexLock );
// RCDB 관련 정리 처리.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
// 현재 저장된 FHS 의 수를 반환.
int CProperFhs::GetFhsCount()
{
return (int)m_vecFHS.size();
}
// 적절한 FHS들의 정보를 전달한다.
// @param [in] nCount FHS정보의 요청 개수.
// @param [out] vecRet 적절한 FHS의 host정보를 vector 타입으로 전달 한다.
// @param [in] listIgnoreFhs 무시할 host정보를 입력한다.
// @return 성공은 return true, 그렇지 않으면 false.
// @note return false를 해도 nCount만큼은 못보내지만 일부 리스트가 전달 될 수 있다.
bool CProperFhs::GetProperFhs( unsigned int nCount, std::vector<std::string> &vecRet, std::list<std::string> &listIgnoreFhs )
{
// CHG 2014-06-24 huibong
// FHS 목록 전달시 m_indexRoundRobin 변수 사용으로 인해 중복된 FHS 가 선택되어 전달될 수 있음.
// 따라서 이에 대한 수정 처리함.
// 결과값을 전달할 vector 참조 객체 초기화.
// - 요청한 수만큼 FHS 가 존재하지 않을 경우 false 를 반환하므로...
// - 호출 모듈에서 문제가 생기지 않도록 미리 Clear 처리
vecRet.clear();
// 요청한 FHS 수가 저장된 FHS 수보다 많은 경우..
// - 동일 FHS 장비에 Content 복제 또는 cache 가 동일하게 발생하여....
// - 동일 파일 Write 에 의한 content 이상 문제를 발생시킬수 있으므로.. 방어 처리.
if( nCount > m_vecFHS.size() )
{
LOG( LERR, "proper fhs count[%zu], but request count[%u]. check flow."
, m_vecFHS.size(), nCount );
return false;
}
// 변수 생성.
std::list<std::string>::iterator itIgnore;
// 중복된 FHS 정보가 push 되지 않기 위한 while loop 체크 횟수 저장 변수
unsigned int iCheckCount = 0;
// Lock 처리
pthread_mutex_lock( &m_mutexLock );
// 내부 Proper FHS 목록 정보가 존재하지 않는 경우.
// - m_vecFHS 의 크기값이 Thread 모듈에 의해 변경될 수 있으므로...
// - Lock 처리 후 판단하는 것으로 위치 수정.
if( m_vecFHS.size() <= 0 )
{
// Lock 해제
pthread_mutex_unlock( &m_mutexLock );
LOG( LWAR, "Proper FHS not exist." );
return false;
}
// 맴버변수인 index 지시자의 오버플로우를 막기 위해 계산 처리.
m_indexRoundRobin %= m_vecFHS.size();
// 요청한 개수 만큼 저장할때까지 반복.
// - 중복 방지를 위해 루프 횟수는 최대 저장된 FHS 횟수 만큼으로 제한
while( vecRet.size() != nCount && iCheckCount < m_vecFHS.size())
{
string strTemp = m_vecFHS.at(m_indexRoundRobin);
// 인덱스 오버플로우를 막기 위함.
m_indexRoundRobin++;
m_indexRoundRobin %= m_vecFHS.size();
++iCheckCount;
// ignore list에 속해 있는 것은 skip 해야한다.
// - 추출된 FHS 가 Ignore 목록에 포함되지 않은 경우에만 결과 vector 에 add 처리
itIgnore = std::find( listIgnoreFhs.begin(), listIgnoreFhs.end(), strTemp );
if( itIgnore == listIgnoreFhs.end() )
{
// 해당 결과 값이 ignore FHS 항목에 포함되지 않은 경우..
// 결과 변수에 Push 처리.
vecRet.push_back( strTemp );
}
}
// Lock 해제
pthread_mutex_unlock( &m_mutexLock );
// 요청한 nCount 만큼 결과 변수에 저장했는지 확인..
// - 요청 수 만큼 저장한 경우.. true 반환.
// - 요청 수 만큼은 아니지만.. 일부 Data 가 저장된 경우에는 false 반환.
if( nCount == vecRet.size() )
return true;
else
return false;
}
+97
View File
@@ -0,0 +1,97 @@
/***************************************************************************
Proper FHS management class
-----------------------------------------
begin : 2010/03/31
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __PROPER_FHS_H__
#define __PROPER_FHS_H__
#include <vector>
#include <list>
#include <string>
#include <pthread.h>
#include "RcdbInfo.h"
// CProperFhs
// RCDB t_fhs_top_status 테이블에 저장된 Available FHS 정보를 주기적으로 갱신 처리
// 각 모듈에 인터페이스 제공을 통해 Available FHS 정보를 전달.
class CProperFhs
{
public:
// 생성자
CProperFhs();
// 소멸자
~CProperFhs();
// 객체 초기화 함수
bool Init();
// thread를 생성하여 업무 flow 를 시작.
bool Start();
// 현재 저장된 FHS 의 수를 반환.
int GetFhsCount();
// 적절한 FHS들의 정보를 전달한다.
// @param [in] nCount FHS정보의 요청 개수.
// @param [out] vecRet 적절한 FHS의 host정보를 vector 타입으로 전달 한다.
// @param [in] listIgnoreFhs 무시할 host정보를 입력한다.
// @return 성공은 return true, 그렇지 않으면 false.
// @note return false를 해도 nCount만큼은 못보내지만 일부 리스트가 전달 될 수 있다.
bool GetProperFhs( unsigned int nCount, std::vector<std::string> &vecRet, std::list<std::string> &listIgnoreFhs );
private:
// available fhs 정보를 저장하기 위한 vector 객체
std::vector<std::string> m_vecFHS;
// 쓰레드 핸들
pthread_t m_threadHandle;
// RCDB 접속 정보를 저장할 변수.
CRcdbInfo m_rcdbInfo;
// ProperFHS를 RoundRobin으로 순서에 따라 전달 하기 위한 변수
int m_indexRoundRobin;
// lock 처리 관련
pthread_mutex_t m_mutexLock;
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
unsigned int m_uSleep;
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 스레드 내에서 각 loop 단위 처리 호출 함수.
bool Execute();
// Proper fhs의 목록을 RCDB t_fhs_top_status 에서 조회.
// CHG 2014-06-24 huibong
// #19042 의 팀회의 결과에 따라
// - RCDB 연결 실패 및 Query 오류 발생시 이전 FHS 목록 유지
// - RCDB 에서 조회된 Proper FHS 가 없는 경우.. Proper FHS 목록 Clear 처리
// - 그외 정상 처리.
bool UpdateFromDB();
};
#endif // __PROPER_FHS_H__
+38
View File
@@ -0,0 +1,38 @@
#include "RcdbInfo.h"
#include "ServiceConfig.h"
// 생성자..
CRcdbInfo::CRcdbInfo()
{
}
// 소멸자..
CRcdbInfo::~CRcdbInfo()
{
}
// CServiceConfig Class 에 저장된 RCDB 접속 정보를 가져와 멤버 변수에 저장한다.
void CRcdbInfo::Load()
{
// CServiceConfig Class 에서 기본적인 설정값에 대해 검사를 수행하므로...
// 본 Class 에서 다시 유효성 검사를 할 필요는 없다.. ( 어차피 유효성 검사 방법이 동일하므로.. )
// RCDB IP
m_strRcdbIp = CServiceConfig::GetInstance()->GetRcdbIp();
// RCDB Port
m_nRcdbPort = CServiceConfig::GetInstance()->GetRcdbPort();
// RCDB DB Name
m_strRcdbName = CServiceConfig::GetInstance()->GetRcdbName();
// RCDB 접근 계정
m_strRcdbAcct = CServiceConfig::GetInstance()->GetRcdbAcct();
// RCDB 접근 Password.
m_strRcdbAcctPw = CServiceConfig::GetInstance()->GetRcdbAcctPw();
return;
}
+52
View File
@@ -0,0 +1,52 @@
/****************************************************************************
RCDB 접속 정보 저장 처리 Class
-----------------------------------------
begin : 2013/11/13
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 __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__ */
+314
View File
@@ -0,0 +1,314 @@
#include "ReplicateControl.h"
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <list>
#include <vector>
#include <string>
#include "Logger.h"
#include "ServiceConfig.h"
#include "ReplicateThread.h"
// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
#define DEFAULT_SLEEP_TIME 2
#define MAX_SLEEP_TIME 5
// 생성자
CReplicateControl::CReplicateControl( CProperFhs * pProperFhs
, CProcessStatus * pProcessStatus
, CDataQueue<CSourceData> * pQueueSource
, CDataQueue<CRetryData> * pQueueRetry
, CDataQueue<CCompleteData> * pQueueComplete )
: m_pProperFhs( pProperFhs )
, m_pProcessStatus( pProcessStatus )
, m_pSourceQueue( pQueueSource )
, m_pRetryQueue( pQueueRetry )
, m_pCompleteQueue( pQueueComplete )
{
// 멤버 변수 초기화
m_threadHandle = 0;
m_uSleep = DEFAULT_SLEEP_TIME;
m_uRetryCount = 2; // 그냥 기본 설정. Init() 함수에서 실제 값으로 설정 처리함.
}
// 소멸자
CReplicateControl::~CReplicateControl()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 객체 초기화 수행
bool CReplicateControl::Init()
{
// Report 로그 처리 관련 멤버 객체 초기화.
// - retry 재시도에 관련된 로깅 처리 목적
if( m_reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
{
std::string strErrorMessage;
m_reportLog.GetLastError( strErrorMessage );
LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
return false;
}
// Queue Pop 제어 관련 멤버 객체 초기화.
if( m_controlQueuePop.Init( m_pSourceQueue, m_pRetryQueue ) == false )
{
LOG( LERR, "CQueuePopControl object init() fail.");
return false;
}
// Retry count 값 설정
m_uRetryCount = CServiceConfig::GetInstance()->GetMaxRetryCount();
return true;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
// return true : 처리할 Data 가 계속 존재하는 경우...
// false : 처리할 Data 가 없어 idle 한 경우.
bool CReplicateControl::MakeReplication()
{
// 1. CProcessStatus에서 생성 가능한 Replication Thread 수 확인
unsigned int nWorkCount = 0;
nWorkCount = m_pProcessStatus->GetValidReplicationCount();
if( nWorkCount == 0 )
{
// 생성 가능한 Replication Thrad 수가 없는 경우...
// - false 를 반환할 경우... MAX_SLEEP_TIME 만큼 대기하므로...
// - 빠른 시간내에 재시도할 수 있도록 true 를 반환.
return true;
}
// 2. Queue 에서 생성 가능한 Thread 수 만큼 Content 정보 pop 처리.
std::list<CRetryData> listWork;
if( m_controlQueuePop.GetWork( nWorkCount, listWork ) == false )
{
// Queue 에 작업할 Data 가 존재하지 않는 경우..
// - idle 상태
return false;
}
// 3. pop 처리된 content 갯수만큼 replication thread 를 생성한다.
std::list<std::string> listIgnoreFHS;
std::vector<std::string> vecFHS;
int nReplicationCount;
while( listWork.empty() == false )
{
// list 에서 Data pop
CRetryData objRetryData;
objRetryData = listWork.front();
listWork.pop_front();
// 제외 대상 FHS 목록을 설정.
// - Replication 성공 FHS 와 원본 FHS 를 제외 대상 목록에 포함시킨다.
listIgnoreFHS.clear();
listIgnoreFHS = objRetryData.m_listSuccessHost;
listIgnoreFHS.push_back( objRetryData.m_OriginData.m_szOriginHostName );
// 복제 횟수 계산.
// - 객체에 저장된 복제 설정값에서 재시도 처리를 통해 이미 생성 완료된 값을 뺀다.
nReplicationCount = objRetryData.m_OriginData.m_nReplicationCount - objRetryData.m_listSuccessHost.size();
if( nReplicationCount <= 0 )
{
// 복제 count 가 0보다 작은 경우...
// - 이상 상황인 경우...
// - 복제 작업을 하지 않고 로깅 처리.
LOG( LWAR, "Replication count[%d] not vaild.[%s]"
, nReplicationCount, objRetryData.m_OriginData.m_szURI.c_str() );
continue;
}
// FHS 를 상태를 관리하는 CProperFHS 모듈을 통해
// - 복제 가능한 FHS 목록을 추출한다.
vecFHS.clear();
m_pProperFhs->GetProperFhs( nReplicationCount, vecFHS, listIgnoreFHS );
// CHG 2014-06-24 huibong
// #19042 의 팀회의 결과에 따라
// - 서비스의 설정된 복제 갯수보다 proper FHS 수가 적은 경우가 발생 가능. 이에 따라 다음과 같이 처리
// - proper FHS 가 0 인 경우 Retry 처리
// - proper FHS 수가 적은 경우
// 일단 추출된 proper FHS 에 복제본 생성 처리
// 향후 신규 개발될 후보정 처리 모듈에서 부족한 복제본에 대한 보정 처리 수행.
if( vecFHS.size() == 0 )
{
// Proper FHS 가 하나도 없는 경우
// - 재시도 횟수가 초과된 경우 Report 로그에 기록 후 삭제 처리.
// - 그외 Retry Queue 에 저장 처리
if( objRetryData.m_uFailCount + 1 > m_uRetryCount )
{
// 재시도 횟수 초과
_LOG( LERR, "CReplicateControl: proper fhs not exist and retry count over.[%s][%llu]"
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
m_reportLog.Write( "ERR", "[%s][Replication fail. check content[%s][%llu]]"
, PROG_NAME
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
}
else
{
// Retry Queue 에 저장 처리
objRetryData.m_uFailCount += 1;
objRetryData.m_timeFailed = time( 0 );
// Retry Queue push 실패 발생 가능
// - Retry Queue 가 full 인 경우 push 실패
// - 만약 무작정 push 될때까지 루프를 돌리면...
// - 본 Control 모듈에서 pop을 할 수 없어 무한 대기가 발생하므로..
// - Push 실패 발생시 report 로그에 기록하고 버린다.
if( m_pRetryQueue->Push( objRetryData ) == false )
{
_LOG( LERR, "CReplicateControl: proper fhs not exist and retry queue push fail.[%s][%llu]"
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
m_reportLog.Write( "ERR", "[%s][Replication fail by retry queue full.check conf[%s][%llu]]"
, PROG_NAME
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
}
else
{
_LOG( LWAR, "CReplicateControl: proper fhs not exist. retry add.[%s][%llu]"
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
}
}
// 다음 Content 에 대해 계속 수행.
continue;
}
// 복제 Thread 생성
// - 이 곳에서는 new로 할당만 처리.
// - CReplicateThread가 종료될때 delete this를 하기때문이니 놀라지말것.
CReplicateThread * pReplicateThread
= new CReplicateThread( m_pProcessStatus, m_pRetryQueue
, m_pCompleteQueue, objRetryData, vecFHS );
// Thread 생성 또는 Start 실패의 경우
// - Retry Queue 에 저장 처리.
if( pReplicateThread == NULL || pReplicateThread->Start() == false )
{
// 최종 Thread 정리
if( pReplicateThread != NULL )
{
delete pReplicateThread;
pReplicateThread = NULL;
}
// 재시도 횟수가 초과된 경우 Report 로그에 기록 후 삭제 처리.
if( objRetryData.m_uFailCount + 1 > m_uRetryCount )
{
// 재시도 횟수 초과
_LOG( LERR, "CReplicateControl: replicate thread start fail and retry count over.[%s][%llu]"
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
m_reportLog.Write( "ERR", "[%s][Replication fail. check content[%s][%llu]]"
, PROG_NAME
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
}
else
{
// Retry Queue 에 저장 처리
objRetryData.m_uFailCount += 1;
objRetryData.m_timeFailed = time( 0 );
// Retry Queue push 실패 발생 가능
// - Retry Queue 가 full 인 경우 push 실패
// - 만약 무작정 push 될때까지 루프를 돌리면...
// - 본 Control 모듈에서 pop을 할 수 없어 무한 대기가 발생하므로..
// - Push 실패 발생시 report 로그에 기록하고 버린다.
if( m_pRetryQueue->Push( objRetryData ) == false )
{
_LOG( LERR, "CReplicateControl: proper fhs not exist and retry queue push fail.[%s][%llu]"
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
m_reportLog.Write( "ERR", "[%s][Replication fail by retry queue full.check conf[%s][%llu]]"
, PROG_NAME
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
}
else
{
_LOG( LWAR, "CReplicateControl: replicate thread start fail. retry add.[%s][%llu]"
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
}
}
}
}
// Pop 한 Content 에 대해 처리가 모두 완료 된 경우...
return true;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CReplicateControl::Execute()
{
if( MakeReplication() == true )
{
m_uSleep = DEFAULT_SLEEP_TIME;
}
else
{
m_uSleep = MAX_SLEEP_TIME;
}
}
// 스레드 함수
void* CReplicateControl::threadFunc( void* arg )
{
CReplicateControl* pObject = reinterpret_cast<CReplicateControl *>(arg);
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
sleep( pObject->m_uSleep );
}
// NEW 2014-06-23 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// thread를 생성하여 업무 flow 를 시작.
bool CReplicateControl::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CReplicateControl::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
+104
View File
@@ -0,0 +1,104 @@
/***************************************************************************
Replicate Control class
-----------------------------------------
begin : 2010/03/31
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __REPLICATE_CONTROL_H__
#define __REPLICATE_CONTROL_H__
#include <pthread.h>
#include "ProperFhs.h"
#include "ProcessStatus.h"
#include "DataQueue.hpp"
#include "ReplicationData.h"
#include "ReportLog.h"
#include "ReplicateQueueControl.h"
// CReplicateControl
// Replication 관련 각 Thread 및 Queue 를 제어하여...
// 전체적으로 Replication 생성 업무 Flow 를 제어한다.
// 내부적으로 Thread를 사용
class CReplicateControl
{
public:
// 생성자
CReplicateControl( CProperFhs * pProperFhs
, CProcessStatus * pProcessStatus
, CDataQueue<CSourceData> * pQueueSource
, CDataQueue<CRetryData> * pQueueRetry
, CDataQueue<CCompleteData> * pQueueComplete );
// 소멸자
~CReplicateControl();
// 객체 초기화 수행
bool Init();
// thread를 생성하여 업무 flow 를 시작.
bool Start();
private:
// Proper FHS 정보를 관리하는 CProperFhs 객체에 대한 포인터.
// - 생성자를 통해 전달받음.
CProperFhs * m_pProperFhs;
// 프로세스 상의 Queue, Thread 상태를 관리하는 CProcessStatus 객체에 대한 포인터
// - 생성자를 통해 전달받음
CProcessStatus * m_pProcessStatus;
// 복제대상 Content 정보를 관리하는 Queue 객체에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CSourceData> * m_pSourceQueue;
// 복제 처리 관련 재시도 Content 정보를 관리하는 Queue 객체에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CRetryData> * m_pRetryQueue;
// 복제 완료된 Content 정보를 관리하는 Queue 객체에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CCompleteData> * m_pCompleteQueue;
// 스레드 핸들
pthread_t m_threadHandle;
// Report log 처리 관련 객체
CReportLog m_reportLog;
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
unsigned int m_uSleep;
// conf 에 설정된 Replication Retry count 정보를 저장하기 위한 멤버 변수
unsigned int m_uRetryCount;
// Queue Pop 처리를 담당할 멤버 객체
CReplicateQueueControl m_controlQueuePop;
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void Execute();
// Replication 생성 처리를 수행하기 위한 함수.
bool MakeReplication();
};
#endif //__REPLICATE_CONTROL_H__
+671
View File
@@ -0,0 +1,671 @@
#include "ReplicateInsert.h"
#include <errno.h>
#include <string.h>
#include "Logger.h"
#include "Database.h"
#include "ServiceConfig.h"
#include "Util.h"
// 처리 loop 간의 sleep time 정의
// - 매 지정된 sec 마다 queue 를 검사하여 RCDB 에 저장 처리.
#define DEFAULT_SLEEP_TIME 3
// RCDB 접속 실패 또는 Query 실패 등의 상황 발생시
#define MAX_SLEEP_TIME 30
// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 2048
// 생성자
// @param [in] pCCompleteData replication 완료 content 정보를 저장하는 queue
CReplicateInsert::CReplicateInsert( CDataQueue<CCompleteData> * pCompleteQueue )
: m_pCompleteQueue( pCompleteQueue )
{
m_threadHandle = 0;
m_uSleep = DEFAULT_SLEEP_TIME;
// NEW 2014-06-16 신규 멤버 변수 초기화 코드 추가.
// Init() 에서 RCDB 접속 실패가 발생 가능하므로.. 구형 RCDB 형상을 사용토록 설정.
m_bServiceTableSeparate = false;
}
// 소멸자
CReplicateInsert::~CReplicateInsert()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 초기화 작업 수행
bool CReplicateInsert::Init()
{
// 생성자를 통해 전달받은 queue 포인터가 NULL 인 경우... 오류 처리
if( m_pCompleteQueue == NULL )
{
LOG( LERR, "replication complete queue is null" );
return false;
}
// Report log 를 처리할 객체 초기화.
if( m_reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
{
std::string strErrorMessage;
m_reportLog.GetLastError( strErrorMessage );
LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
return false;
}
// RCDB 접속 정보 Load.
m_rcdbInfo.Load();
// 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 )
{
LOG( LERR, "RCDB connection failed." );
return false;
}
else
{
// 접속 성공한 경우...
LOG( LINF, "RCDB connection test ok..." );
// NEW 2014-06-19 huibong
// 서비스 table 분리된 신규 형상인지.. 구형 형상인지 체크하여
// m_bServiceTableSeparate 변수 설정 처리.
// 해당 변수 초기화는 생성자에서 1차로 수행함.
char szQuery[1024];
snprintf( szQuery, 1023,
"SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource' " );
rcdb.PgDoExec( szQuery );
if( rcdb.PgResult( DataBase::NOT_CLEAR ) < 0 )
{
// Query 수행 결과 오류 발생시...
// - pg_tables 가 존재하지 않을 경우 오류 발생 가능...
// - 이는 RCDB 가 아직 적절하게 구성되지 않았다는 의미이므로... Init()함수를 오류로 처리한다.
LOG( LERR, "RCDB t_dav_resource exist check failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
else
{
// Query 수행이 정상인 경우.
int nResult = rcdb.GetNoTuples();
if( nResult > 0 )
{
// t_dav_resource 테이블이 존재하는 경우...
m_bServiceTableSeparate = false;
_LOG( LINF, "CReplicateInsert: t_dav_resource table exist.[Old RCDB Type]" );
}
else
{
// t_dav_resource 테이블이 존재하지 않는 서비스별 테이블 분리 형상인 경우.
m_bServiceTableSeparate = true;
_LOG( LINF, "CReplicateInsert: t_dav_resource table not exist.[New RCDB Type]" );
}
}
// RCDB 조회 결과 set 를 clear 처리 후 RCDB 접속 해제.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
}
// Thread 를 생성하여 할당된 작업을 수행.
bool CReplicateInsert::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CReplicateInsert::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// Thread function
void* CReplicateInsert::threadFunc( void* arg )
{
CReplicateInsert* pObject = reinterpret_cast<CReplicateInsert *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
// 1회 수행 후 지정된 시간 만큼 대기
sleep( pObject->m_uSleep );
}
// NEW 2014-06-19 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CReplicateInsert::Execute()
{
// Queue 상에 처리할 Data 가 존재할 경우...
if( m_pCompleteQueue->GetSize() > 0 )
{
bool bResult;
// t_dav_resource 테이블 사용 형상인 경우.
if( m_bServiceTableSeparate == false )
{
bResult = InsertToOldDB();
}
else
{
// 서비스별 Table 이 분리된 신규 형상인 경우.
bResult = InsertToDB();
}
// 처리 결과에 따라 loop sleep time 조정
if( bResult == true )
m_uSleep = DEFAULT_SLEEP_TIME;
else
{
// RCDB 연결 실패, Queury 수행 실패 등의 오류 발생시
m_uSleep = MAX_SLEEP_TIME;
}
}
else
{
// queue 상에 처리할 Data 가 존재하지 않는 경우.
m_uSleep = DEFAULT_SLEEP_TIME;
}
}
// RCDB 상에 복제 완료된 content 정보를 insert 처리한다. ( t_dav_resource 사용 )
bool CReplicateInsert::InsertToOldDB()
{
// 1. RCDB 와 연결 처리.
DataBase 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 )
{
// RCDB 접속이 실패
LOG( LERR, "RCDB connection failed." );
return false;
}
// 2. 루프를 돌면서 queue 에 저장된 data 를 처리한다.
// queue 에서 꺼낸 정보를 저장하기 위한 변수
CCompleteData resultData;
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
// Query 수행 결과값 저장을 위한 변수..
int nResult = 0;
while( m_pCompleteQueue->GetSize() > 0 )
{
// queue 에 저장된 첫번째 data 복사 (queue 에서 삭제된 상태 아님)
resultData = m_pCompleteQueue->Front();
// URI의 '(작은따옴표) 문자가 있을 경우 SQL 오류 방지를 위하여 관련 문자를 다르게 변경함
std::string orgURI = stringreplace(resultData.m_OriginData.m_szURI, "'", "''");
// 원본 조회 서브 쿼리 절에서 분리함 2012.02.10
// PGPool 경우 INSERT~SELECT 사용 지향
// 해당 서브절에 SELECT 값이 DB01,DB02 서로 다른 값을 참조될 수 있다.
// 3. 원본 존재 여부 확인
// Replication 처리가 완료된 Content 에 대해 meta 정보 입력전 실제 원본이 존재하는지 확인한다.
sprintf( szQuery,
"SELECT "
" sp_user_seq, sp_svc_tran_id, "
" '%s', "
" resource_type, uri, filename_org, '%s', "
" depth, display_name, get_content_language, "
" get_content_length, get_content_type, "
" get_etag, get_lastmodified, creation_date, "
" lock_discovery, source, supported_lock, deleted_yn, 'R', file_lastmodified " // is_cache = R
"FROM t_dav_resource "
"WHERE filename_hash = '%s' "
"AND uri = '%s' "
"AND get_content_length = %lld "
"AND is_cache = 'N' AND host_name = '%s' ;",
resultData.m_szTargetHostName.c_str(),
resultData.m_szTargetFileNameHash.c_str(),
resultData.m_OriginData.m_szFileNameHash.c_str(),
orgURI.c_str(),
resultData.m_OriginData.m_nContentLength,
resultData.m_OriginData.m_szOriginHostName.c_str() );
// Query 수행 및 결과 확인
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// RCDB t_dav_resource 테이블 조회 실패시
// - RCDB 연결 관련 실패시 : nResult = -4
// - uri 상의 특수문자로 인해 query 오류 : nResult = -4
// - 즉 오류 반환값이 동일하기 때문에 어떠한 문제로 query 가 실패했는지 판단 못함.
// 우선 DB 연결 문제일 수 있으므로...
// RCDB 재접속 시도.
rcdb.PgClear();
rcdb.PgCloseDB();
sleep( 1 );
if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
{
// RCDB 접속 실패 문제인 경우..
// - 본 처리 함수 종료 처리하여 다음 loop 에서 재시도토록 처리.
LOG( LERR, "select query fail because RCDB connection error. [%d][%s]"
, nResult, rcdb.GetErrorMessage().c_str());
rcdb.PgCloseDB();
return false;
}
else
{
// uri 상에 특수 문자 문제로 query 실패시
// - Report 로그에 관련 내역 기록
// - queue 에서 해당 정보를 삭제 처리한 후 계속 수행한다.
LOG( LERR, "select query fail. queue forced pop.[%s][%d][%s]"
, orgURI.c_str(), nResult, rcdb.GetErrorMessage().c_str() );
m_reportLog.Write( "WAR", "[%s][Replication contents RCDB insert fail. check uri.[%s]]"
, PROG_NAME, orgURI.c_str());
rcdb.PgClear();
m_pCompleteQueue->Pop();
continue;
}
}
// 결과 Row 수 확인.
nResult = rcdb.GetNoTuples();
if( nResult <= 0 )
{
// Replication 생성된 content 가 RCDB 에 존재하지 않는 경우...
// - RCDB 에 저장하지 않고 그냥 버린다.
// - 이로 인해 FHS 에 meta 기준으로 삭제 불가능한 파일이 남게 된다.
_LOG( LINF, "Replication content[%s] not found in RCDB.", orgURI.c_str() );
rcdb.PgClear();
m_pCompleteQueue->Pop();
continue;
}
// 조회된 원본 정보를 list 에 저장
std::vector<string> srcinfo;
std::string strResult;
for( int j = 0 ; j < 21; j++ )
{
strResult = rcdb.GetValue( 0, j );
strResult = stringreplace( strResult, "'", "''" );
srcinfo.push_back( strResult );
}
// Select Query 의 결과를 Clear
rcdb.PgClear();
// 4. Replication 된 Content 정보를 Meta 정보로 Insert
// INSERT 작업시 앞에서 조회된 원본 정보를 이용하여 INSERT 한다.
sprintf( szQuery,
"INSERT INTO t_dav_resource "
"( "
" sp_user_seq, sp_svc_tran_id, "
" host_name, "
" resource_type, uri, filename_org, filename_hash, "
" depth, display_name, get_content_language, "
" get_content_length, get_content_type, "
" get_etag, get_lastmodified, creation_date, "
" lock_discovery, source, supported_lock, deleted_yn, is_cache ,file_lastmodified) "
"VALUES "
"( "
" %s, %s, "
" '%s', "
" %s, '%s', '%s', '%s', "
" %s, '%s', '%s', "
" %s, '%s', "
" '%s', %s, %s, "
" '%s', %s, '%s', '%s', '%s', %s );", // is_cache = R
srcinfo[0].c_str(),srcinfo[1].c_str(),srcinfo[2].c_str(),srcinfo[3].c_str(),
srcinfo[4].c_str(),srcinfo[5].c_str(),srcinfo[6].c_str(),srcinfo[7].c_str(),
srcinfo[8].c_str(),srcinfo[9].c_str(),srcinfo[10].c_str(),srcinfo[11].c_str(),
srcinfo[12].c_str(),srcinfo[13].c_str(),srcinfo[14].c_str(),srcinfo[15].c_str(),
srcinfo[16].c_str(),srcinfo[17].c_str(),srcinfo[18].c_str(),srcinfo[19].c_str(),srcinfo[20].c_str()
);
// INSERT query 수행
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::CLEAR );
if( nResult < 0 )
{
// RCDB INSERT 작업시 오류가 발생한 경우...
// - uri 문제는 앞의 SELECT query 에서 체크했으므로 아니고....
// - DB 연결 문제 역시 바로 앞에서 체크 했으므로 아닐 가능성이 큼.
// - 가장 큰 오류는 Query 오류일 가능성이 크므로...
// - Queue 에 pop 처리한다.
// Report 로그에 관련 내역 기록
LOG( LERR, "insert query fail. queue forced pop.[%s][%d][%s]"
, orgURI.c_str(), nResult, rcdb.GetErrorMessage().c_str() );
m_reportLog.Write( "WAR", "[%s][Replication contents RCDB insert fail. Check query[%s][%s]]"
, PROG_NAME, orgURI.c_str(), rcdb.GetErrorMessage().c_str() );
// queue 에서 해당 정보를 삭제 처리한 후 계속 수행한다.
rcdb.PgClear();
m_pCompleteQueue->Pop();
continue;
}
rcdb.PgClear();
// NEW 2012-09-24 huibong Replication 최종 정합성 검증 기능 추가
// 원본 존재 여부를 확인한 후 Insert 를 수행하지만...
// 해당 interval 동안 원본이 MOVE 또는 Rename, Delete 되는 상황이 발생하여 잘못된 Replication이 발생하고..이로 인해 서비스 장애가 발생 가능
// INSERT 작업 후 원본에 대한 정합성 여부를 다시 한번 확인하여
// 원본이 변경된 경우 INSERT 된 Meta 정보를 DELETE 처리한다.
// 5. INSERT 된 Meta 정보에 대해 원본이 존재하는지 다시 한번 확인하여
// 원본이 변경된 경우 INSERT 된 정보를 DELETE 한다. (deleted_yn = 'Y', 물리 Content 자동 제거 처리 목적 )
sprintf( szQuery,
"SELECT "
" uri, get_content_length, deleted_yn "
"FROM t_dav_resource "
"WHERE filename_hash = '%s' "
"AND uri = '%s' "
"AND get_content_length = %lld "
"AND is_cache = 'N' AND host_name = '%s' AND deleted_yn = '%s' ;",
resultData.m_OriginData.m_szFileNameHash.c_str(),
orgURI.c_str(),
resultData.m_OriginData.m_nContentLength,
resultData.m_OriginData.m_szOriginHostName.c_str(),
srcinfo[18].c_str()
);
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// 최종 검증을 위한 Check query 가 실패한 경우..
// query 구문은 앞의 2번 query 를 통해 검증되었으므로.. query 문제가 아닐 듯.
// 본 오류는 대부분 RCDB 연결 관련 문제인 듯.
// - 로깅 처리 후
// - RCDB 에 이미 INSERT 처리 되었으므로... queue pop 처리
// - DB 세션 종료 처리 후 .. return 하여.. sleep 후 다음 루프가 동작하도록 처리.
LOG(LERR, "replication last check query fail.[%s][%d][%s][%s]"
, orgURI.c_str(), nResult, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
m_pCompleteQueue->Pop();
return false;
}
// 조회된 결과 row 수 확인
nResult = rcdb.GetNoTuples();
rcdb.PgClear();
if( nResult <= 0 )
{
// 원본이 변경된 경우......
// 앞에서 INSERT 된 Replication 정보를 deleted_yn = 'Y' 처리한다.
LOG( LWAR, "replication check source not found. insert replication value set deleted [%s]"
, orgURI.c_str() );
sprintf( szQuery,
"UPDATE t_dav_resource SET deleted_yn = 'Y', get_lastmodified = %lld "
"WHERE uri = '%s' "
" AND host_name = '%s' "
" AND filename_hash = '%s' "
" AND get_content_length = %lld "
" AND is_cache = 'R' "
" AND deleted_yn = 'N' ;",
longtime_now(),
orgURI.c_str(),
resultData.m_szTargetHostName.c_str(),
resultData.m_szTargetFileNameHash.c_str(),
resultData.m_OriginData.m_nContentLength );
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::CLEAR );
if( nResult < 0 )
{
// 삭제 처리 작업이 실패한 경우...
// RCDB 연결 문제로 인한 가능성이 가장 크므로...
// - 로깅 후..
// - RCDB 연결 해제...
// - queue pop 후 다음 루프에서 처리토록 return 처리.
LOG( LERR, "invalid replication delete fail.[%s][%s]"
, orgURI.c_str(), rcdb.GetErrorMessage().c_str());
m_reportLog.Write( "WAR", "[%s][Invalid replication contents exist.[%s]]"
, PROG_NAME, orgURI.c_str() );
rcdb.PgClear();
rcdb.PgCloseDB();
m_pCompleteQueue->Pop();
return false;
}
}
// 1 개 replication content 에 대한 RCDB insert 작업이 완료된 경우.
// - RCDB clear 처리후
// - queue pop.
rcdb.PgClear();
m_pCompleteQueue->Pop();
_LOG( LDBG, "replication rcdb Insert [%s|%s]->[%s|%s|%llu]"
, resultData.m_OriginData.m_szOriginHostName.c_str()
, resultData.m_OriginData.m_szURI.c_str()
, resultData.m_szTargetHostName.c_str()
, resultData.m_szTargetFileNameHash.c_str()
, resultData.m_OriginData.m_nContentLength
);
}
// queue 에 저장된 정보 처리 작업이 완료된 경우...
// - RCDB 연결 해제 처리.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
// RCDB 상에 복제 완료된 content 정보를 insert 처리한다. ( t_meta_xxx 사용 )
// - 신규 RCDB 관련 미사용 Column 항목 제거 처리.
bool CReplicateInsert::InsertToDB()
{
// 1. RCDB 와 연결 처리.
DataBase 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 )
{
// RCDB 접속이 실패
LOG( LERR, "RCDB connection failed." );
return false;
}
// 2. 루프를 돌면서 queue 에 저장된 data 를 처리한다.
// queue 에서 꺼낸 정보를 저장하기 위한 변수
CCompleteData resultData;
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
// Query 수행 결과값 저장을 위한 변수..
int nResult = 0;
while( m_pCompleteQueue->GetSize() > 0 )
{
// queue 에 저장된 첫번째 data 복사 (queue 에서 삭제된 상태 아님)
resultData = m_pCompleteQueue->Front();
// URI의 '(작은따옴표) 문자가 있을 경우 SQL 오류 방지를 위하여 관련 문자를 다르게 변경함
std::string orgURI = stringreplace( resultData.m_OriginData.m_szURI, "'", "''" );
// 원본 조회 서브 쿼리 절에서 분리함 2012.02.10
// PGPool 경우 INSERT~SELECT 사용 지향
// 해당 서브절에 SELECT 값이 DB01,DB02 서로 다른 값을 참조될 수 있다.
// CHG 2015-02-12 huibong
// 신규 RCDB 형상에 맞도록 처리 Flow 단순화 (#21354)
// - RCDB 구조가 pgpool -> streaming replicaton 구조로 변경됨
// - 이에 따라 기존 SELECT 후 INSERT, 검수, DELETE flow 에 대한 단순화 가능함.
sprintf( szQuery,
"INSERT INTO t_meta_%s " // 신형 RCDB 형상
"( "
" sp_user_seq, sp_svc_tran_id, "
" host_name, "
" resource_type, uri, filename_org, filename_hash, "
" depth, display_name, "
" get_content_length, get_content_type, "
" get_lastmodified, creation_date, "
" source, deleted_yn, is_cache , file_lastmodified "
") "
"SELECT "
" sp_user_seq, sp_svc_tran_id, "
" '%s', "
" resource_type, uri, filename_org, '%s', "
" depth, display_name, "
" get_content_length, get_content_type, "
" get_lastmodified, creation_date, "
" source, deleted_yn, 'R', file_lastmodified " // is_cache = R
"FROM t_meta_%s " // 신형 RCDB 형상
"WHERE filename_hash = '%s' "
"AND uri = '%s' "
"AND get_content_length = %lld "
"AND is_cache = 'N' AND host_name = '%s' ;"
, resultData.m_OriginData.m_szServiceSeq.c_str()
, resultData.m_szTargetHostName.c_str()
, resultData.m_szTargetFileNameHash.c_str()
, resultData.m_OriginData.m_szServiceSeq.c_str()
, resultData.m_OriginData.m_szFileNameHash.c_str()
, orgURI.c_str()
, resultData.m_OriginData.m_nContentLength
, resultData.m_OriginData.m_szOriginHostName.c_str()
);
// INSERT-SELECT query 수행
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// RCDB SELECT-INSERT 작업시 오류가 발생한 경우...
// - RCDB 연결 관련 실패시 : nResult = -4
// - uri 상의 특수문자로 인해 query 오류 : nResult = -4
// - 그외 Query 오류일 가능성이 존재
std::string strPreviousErrorMessage = rcdb.GetErrorMessage();
// 우선 DB 연결 문제일 수 있으므로... RCDB 재접속 시도.
rcdb.PgClear();
rcdb.PgCloseDB();
sleep( 1 );
if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
{
// RCDB 접속 실패 문제인 경우..
// - 본 처리 함수 종료 처리하여 다음 loop 에서 재시도토록 처리.
LOG( LERR, "Query fail because New RCDB connection error. [%d][%s]"
, nResult, rcdb.GetErrorMessage().c_str() );
rcdb.PgCloseDB();
return false;
}
else
{
// uri 상에 특수 문자 또는 Query 문제인 경우...
// - Report 로그에 관련 내역 기록
// - queue 에서 해당 정보를 삭제 처리한 후 계속 수행한다.
LOG( LERR, "insert-select query fail. queue forced pop.[%s][%d][%s]"
, orgURI.c_str(), nResult, strPreviousErrorMessage.c_str() );
// Report 로그에 관련 내역 기록
m_reportLog.Write( "WAR", "[%s][Replication contents New RCDB insert fail. Check query[%s][%s]]"
, PROG_NAME, orgURI.c_str(), strPreviousErrorMessage.c_str() );
rcdb.PgClear();
m_pCompleteQueue->Pop();
continue;
}
}
// PgResult 호출시 NOT_CLEAR 옵션을 사용했으므로....INSERT 수행 row 수 확인을 위해 GetCmdTuples() 함수를 호출가능.
nResult = rcdb.GetCmdTuples();
if( nResult > 0 )
{
_LOG( LDBG, "replication rcdb Insert [%d] row, [%s|%s]->[%s|%s|%llu]"
, nResult
, resultData.m_OriginData.m_szOriginHostName.c_str()
, resultData.m_OriginData.m_szURI.c_str()
, resultData.m_szTargetHostName.c_str()
, resultData.m_szTargetFileNameHash.c_str()
, resultData.m_OriginData.m_nContentLength
);
}
else
{
// INSERT 처리된 row 가 없는 경우....
// - 생성된 replicaton 파일은 고아 파일이 됨.
_LOG( LWAR, "replication rcdb Insert origin not found. orgin[%s|%s|%llu|%s] orphan[%s|%s|%llu]"
, resultData.m_OriginData.m_szOriginHostName.c_str()
, resultData.m_OriginData.m_szFileNameHash.c_str()
, resultData.m_OriginData.m_nContentLength
, resultData.m_OriginData.m_szURI.c_str()
, resultData.m_szTargetHostName.c_str()
, resultData.m_szTargetFileNameHash.c_str()
, resultData.m_OriginData.m_nContentLength
);
}
// 1 개 replication content 에 대한 RCDB insert 작업이 완료된 경우.
// - RCDB clear 처리후
// - queue pop.
rcdb.PgClear();
m_pCompleteQueue->Pop();
}
// queue 에 저장된 정보 처리 작업이 완료된 경우...
// - RCDB 연결 해제 처리.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
+85
View File
@@ -0,0 +1,85 @@
/***************************************************************************
Replication 완료된 Content 정보를 RCDB 에 저장
-----------------------------------------
begin : 2010/03/12
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __REPLICATE_INSERT_H__
#define __REPLICATE_INSERT_H__
#include <pthread.h>
#include "ReplicationData.h"
#include "DataQueue.hpp"
#include "RcdbInfo.h"
#include "ReportLog.h"
// CReplicateInsert
// Replication 생성이 완료된 Conetent 정보를 queue 에서 pop 하여 RCDB 에 저장 처리를 수행
// 내부적으로 Thread를 사용
class CReplicateInsert
{
public :
// 생성자
// @param [in] pCCompleteData replication 완료 content 정보를 저장하는 queue
CReplicateInsert( CDataQueue<CCompleteData> * pCCompleteData );
// 소멸자
~CReplicateInsert();
// 초기화
bool Init();
// Thread를 생성하고, 이를 시작한다.
bool Start();
private :
// 스레드 함수
static void* threadFunc( void* arg );
// RCDB 상에 복제 완료된 content 정보를 insert 처리한다. ( t_dav_resource 사용 )
bool InsertToOldDB();
// RCDB 상에 복제 완료된 content 정보를 insert 처리한다. ( t_meta_xxx 사용 )
bool InsertToDB();
void Execute();
private:
// 생성자를 통해 전달받은 replication 생성 완료된 content 정보를 저장하는 queue 포인터.
CDataQueue<CCompleteData> * m_pCompleteQueue;
// 쓰레드 핸들
pthread_t m_threadHandle;
// RCDB 접속 정보를 저장할 변수.
CRcdbInfo m_rcdbInfo;
// New 2014-06-19 huibong
// RCDB 형상변경에 따라 서비스별 Table 분리 여부 판단 flag
// TODO: 향후 신규 RCDB 형상 적용 완료 후 본 항목 제거 필요.
// - false : 기존 t_dav_resource 테이블 사용.
// - ture : 신규 RCDB 형상 (서비스별 meta table 분리) 사용.
bool m_bServiceTableSeparate;
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
unsigned int m_uSleep;
// Report log 처리 관련 객체
CReportLog m_reportLog;
};
#endif // __REPLICATE_INSERT_H__
+141
View File
@@ -0,0 +1,141 @@
#include "ReplicateQueueControl.h"
#include <time.h>
#include "Logger.h"
#include "ServiceConfig.h"
// Retry 대상 Content 에 대해 재작업 처리 최소 시간 간격.
#define RETRY_CHECK_TIME 300 // 5분
// 생성자
CReplicateQueueControl::CReplicateQueueControl()
{
// 멤버 변수 초기화
m_pSourceQueue = NULL;
m_pRetryQueue = NULL;
}
// 소멸자
CReplicateQueueControl::~CReplicateQueueControl()
{
}
// 객체 초기화 함수
bool CReplicateQueueControl::Init( CDataQueue<CSourceData> * pSourceQueue, CDataQueue<CRetryData> * pRetryQueue )
{
// 전달받은 인자를 멤버변수에 할당
m_pSourceQueue = pSourceQueue;
m_pRetryQueue = pRetryQueue;
// Report 로그 처리 관련 멤버 객체 초기화.
if( m_reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
{
std::string strErrorMessage;
m_reportLog.GetLastError( strErrorMessage );
LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
return false;
}
return true;
}
// Replication 을 처리할 객체를 요청한 수만큼 list 형태로 전달
// @param [in] nCnt 요청 개수를 입력한다.
// @param [out] lstWork 작업대상 정보를 list형태로 전달
// @return true 처리할 data 가 존재하는 경우..
// false 처리할 data 가 없는 경우
bool CReplicateQueueControl::GetWork(unsigned int nCnt, std::list<CRetryData>& lstWork)
{
// 업무 Flow 상 nCnt == 0 여부는 호출 모듈에서 이미 처리했으므로...
// 본 함수에서는 확인하지 않는다.
// 1. Data 를 저장할 객체 Clear 처리.
lstWork.clear();
// 2. Retry Queue 상에서 다음 조건을 만족하는 Content 가 존재하는 경우 push 처리
// - 재시도 횟수가 설정값 이하이고... (초과된 경우.. 제거 처리)
// - 지정된 시간 이상 지났는지...
// - Retry Queue 는 특성상 가장 오래된 Data 가 먼저 pop 되므로...
// 위의 조건을 만족하지 않는 Data 가 나타나는 경우... push 를 중지한다.
CRetryData objRetryData;
time_t timeNow;
// Retry Queue 에 Data 가 존재하는 경우..
while( m_pRetryQueue->GetSize() > 0 && nCnt > lstWork.size() )
{
// Retry Queue 에서 첫번째 Data를 가져와.
objRetryData = m_pRetryQueue->Front();
// 재시도 횟수를 초과했는지 확인
if( objRetryData.m_uFailCount > CServiceConfig::GetInstance()->GetMaxRetryCount() )
{
// 재시도 횟수를 초과한 경우...
// - Report 로그에 내역 기록한 후
// - queue 에서 pop 하여 제거 처리
_LOG( LERR, "CReplicateQueueControl: retry count over. replication fail.[%s][%llu]"
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength);
m_reportLog.Write( "ERR", "[%s][Replication fail. check content[%s][%llu]]"
, PROG_NAME
, objRetryData.m_OriginData.m_szURI.c_str()
, objRetryData.m_OriginData.m_nContentLength );
m_pRetryQueue->Pop();
continue;
}
// 재시도 가능 시간( Replication 실패 후 지정된 시간 이상 지났는지)인지 확인
// - 시간이 지나지 않았다면 더이상 m_pRetryQueue는 조사할 필요가 없다.
time(&timeNow);
if( objRetryData.m_timeFailed == 0 )
{
LOG( LERR, "time value is zero. check flow." );
}
if( (timeNow - objRetryData.m_timeFailed) < RETRY_CHECK_TIME )
{
// 재시도 가능 시간이 아닌 경우.. queue를 그대로 유지하고 loop를 빠져나간다.
break;
}
// 재시도 해야 하는 content 인 경우...
// - 결과 변수에 push 처리하고
// - retry queue 에서는 pop 하여 제거 처리
lstWork.push_back( objRetryData );
m_pRetryQueue->Pop();
}
// 3. Retry Queue 에서 요청한 만큼 Data 를 pop 하지 못한 경우...
// - source queue 에서 pop 처리.
CSourceData objSourceData;
while( nCnt > lstWork.size() && m_pSourceQueue->GetSize() > 0 )
{
// 대상 정보를 가져온다.
objSourceData = m_pSourceQueue->Front();
// list에 넣어주기위해 CRetryData타입으로 생성한다.
objRetryData.m_OriginData = objSourceData;
objRetryData.m_uFailCount = 0;
objRetryData.m_listSuccessHost.clear();
objRetryData.m_timeFailed = 0;
// 결과 변수에 push 처리하고... source queue 에서 pop 처리.
lstWork.push_back(objRetryData);
m_pSourceQueue->Pop();
}
//_LOG( LINF, "CReplicateQueueControl: queue pop count [%zu]", lstWork.size() );
// 결과 list 에 data 가 존재하는 경우...
if( lstWork.empty() == false )
return true;
else
return false;
}
+64
View File
@@ -0,0 +1,64 @@
/***************************************************************************
Replicationg 관련 Queue Pop Control class
-----------------------------------------
begin : 2010/03/31
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __REPLICATE_QUEUE_CONTROL_H__
#define __REPLICATE_QUEUE_CONTROL_H__
#include <list>
#include "DataQueue.hpp"
#include "ReplicationData.h"
#include "ReportLog.h"
// CQueuePopControl
// Replication 처리 관련 원본 Queue 와 재시도 Queue 중
// 어느 Queue 에서 Replication 대상 content 를 POP 처리할 것인지를
// 제어하기 위한 클래스
class CReplicateQueueControl
{
public:
// 생성자
CReplicateQueueControl();
// 소멸자
~CReplicateQueueControl();
// 객체 초기화 함수
bool Init( CDataQueue<CSourceData> * pSourceQueue, CDataQueue<CRetryData> * pRetryQueue );
// Replication 을 처리할 객체를 요청한 수만큼 list 형태로 전달
// @param [in] nCnt 요청 개수를 입력한다.
// @param [out] lstWork 작업대상 정보를 list형태로 전달
// @return true 처리할 data 가 존재하는 경우..
// false 처리할 data 가 없는 경우
bool GetWork(unsigned int nCnt, std::list<CRetryData>& lstWork);
private:
// 복제대상 Content 정보를 관리하는 Queue 객체에 대한 포인터
CDataQueue<CSourceData> * m_pSourceQueue;
// 복제 처리 관련 재시도 Content 정보를 관리하는 Queue 객체에 대한 포인터
CDataQueue<CRetryData> * m_pRetryQueue;
// Report log 처리 관련 객체
CReportLog m_reportLog;
};
#endif // __REPLICATE_QUEUE_CONTROL_H__
+817
View File
@@ -0,0 +1,817 @@
#include "ReplicateSelect.h"
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "Logger.h"
#include "Database.h"
#include "ServiceConfig.h"
#include "Util.h"
// Replication 대상 조회시...
// 현재 기준 얼마 이전에 업로드, 변경된 Content 를 대상으로 할 것인지에 대한 최대, 최소값
// 기본적으로 conf 의 TARGET_SELECT_INTERVAL 설정값을 사용
// 단위 : second
#define MAX_TARGET_SELECT_INTERVAL 1800 // 30 분 이전 content 까지 대상으로 판단
#define MIN_TARGET_SELECT_INTERVAL 60 // 1 분 이전 content 까지 대상으로 판단
// 프로세스 구동시 Replication 대상 선택 MAX, MIN 시간 ( hour 단위 )
#define MIN_TARGET_SELECT_INTERVAL_ON_STARTING 1
// CHG 2014-07-16 huibong
// 팀 회의 결과.. replication target select 주기는
// 기존의 복잡한 계산 로직 대신....고정값으로 sleep 처리토록 수정한다.
// - 짧은 sleep 은 DB 부하만 발생시키고.. queue 처리 등을 고려했을 때 큰 의미가 없다고 판단.
// Replicatin 처리시 매 Loop 마다 Sleep 시간 정보 정의 ( second 단위)
#define DEFAULT_SLEEP_TIME 60
// RCDB 접속 실패 또는 Query 실패 등의 상황 발생시
#define MAX_SLEEP_TIME 60
// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 1024
////////////////////////////////////////////
// 생성자
CReplicateSelect::CReplicateSelect( CDataQueue<CSourceData> * pQueue )
: m_pSourceQueue( pQueue )
, m_timeLastCheck(0)
{
SetTargetSelectInterval( MAX_TARGET_SELECT_INTERVAL );
SetTargetSelectIntervalOnStarting( MIN_TARGET_SELECT_INTERVAL_ON_STARTING );
SetSleepTimeDefault();
m_threadHandle = 0;
// NEW 2014-06-16 신규 멤버 변수 초기화 코드 추가.
// Init() 에서 RCDB 접속 실패가 발생 가능하므로.. 구형 RCDB 형상을 사용토록 설정.
m_bServiceTableSeparate = false;
}
CReplicateSelect::~CReplicateSelect()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 초기화 작업 수행
bool CReplicateSelect::Init()
{
// 조회 정보를 저장할 Queue 포인터가 NULL 경우 오류 처리
if( m_pSourceQueue == NULL )
{
LOG( LERR, "replication queue is null");
return false;
}
// Queue 정보 초기화 처리.
m_pSourceQueue->Init();
// 2014-06-13 huibong
// config 객체 변경 및 Skip Keyword 처리 관련 멤버 변수화에 따른 초기화 처리.
m_vecSipkWord = CServiceConfig::GetInstance()->GetSkipWord();
// RCDB 접속 정보 Load.
m_rcdbInfo.Load();
// 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 )
{
LOG( LERR, "RCDB connection failed.");
return false;
}
else
{
// 접속 성공한 경우...
LOG( LINF, "RCDB connection test ok..." );
// NEW 2014-06-13 huibong
// 서비스 table 분리된 신규 형상인지.. 구형 형상인지 체크하여
// m_bServiceTableSeparate 변수 설정 처리.
// 해당 변수 초기화는 생성자에서 1차로 수행함.
char szQuery[1024];
snprintf( szQuery, 1023,
"SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource' " );
rcdb.PgDoExec( szQuery );
if( rcdb.PgResult( DataBase::NOT_CLEAR ) < 0 )
{
// Query 수행 결과 오류 발생시...
// - pg_tables 가 존재하지 않을 경우 오류 발생 가능...
// - 이는 RCDB 가 아직 적절하게 구성되지 않았다는 의미이므로... Init()함수를 오류로 처리한다.
LOG( LERR, "RCDB t_dav_resource exist check failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
return false;
}
else
{
// Query 수행이 정상인 경우.
int nResult = rcdb.GetNoTuples();
if( nResult > 0 )
{
// t_dav_resource 테이블이 존재하는 경우...
m_bServiceTableSeparate = false;
_LOG( LINF, "CReplicateSelect: t_dav_resource table exist.[Old RCDB Type]" );
}
else
{
// t_dav_resource 테이블이 존재하지 않는 서비스별 테이블 분리 형상인 경우.
m_bServiceTableSeparate = true;
_LOG( LINF, "CReplicateSelect: t_dav_resource table not exist.[New RCDB Type]" );
}
}
// RCDB 조회 결과 set 를 clear 처리 후 RCDB 접속 해제.
rcdb.PgClear();
rcdb.PgCloseDB();
return true;
}
}
// Thread 를 생성하여 할당된 작업을 수행.
bool CReplicateSelect::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CReplicateSelect::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
bool CReplicateSelect::Start( unsigned int interval, unsigned int intervalOnStarting )
{
SetTargetSelectInterval( interval );
SetTargetSelectIntervalOnStarting( intervalOnStarting );
return this->Start();
}
// Thread function
void * CReplicateSelect::threadFunc( void * arg )
{
CReplicateSelect* pObject = reinterpret_cast<CReplicateSelect *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pthread_testcancel();
pObject->Execute();
pthread_testcancel();
sleep( pObject->m_timeSleep ); // 멤버변수에 설정된 시간만큼 Sleep 처리
}
// NEW 2014-06-13 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
// CHG 2014-06-13 huibong
// detach 된 Thread 에 대해 pthread_exit() 호출시
// 기준 할당 자원에 대한 해지 처리가 되지 않아 메모리 누수 발생 가능( FreeBSD ftsd 발생)
// 따라서 이를 해결하기 detach 된 thread 에 대해 pthread_exit() 호출 코드 제거.
//pthread_exit(NULL);
return NULL;
}
// loop 단위의 replication 대상 조회 작업 수행
void CReplicateSelect::Execute()
{
// DB 조회 처리 함수 호출
SelectFromDB();
// Logging
_LOG( LINF, "Replication target select complete. last check time[%lld], sleep[%u] sec", m_timeLastCheck, m_timeSleep );
}
// RCDB 로 부터 Replication 대상 Content 를 조회한 후...
// 해당 정보를 Queue 상에 저장한다.
bool CReplicateSelect::SelectFromDB()
{
// 1. 현재 시간정보를 구한다.( micro-second 단위)
long long timeNow = longtime_now();
if (timeNow < 0)
{
LOG( LERR, "longtime_now function error [%lld]", timeNow);
return false;
}
// 2. RCDB 와 연결 처리.
DataBase 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 )
{
LOG( LERR, "RCDB connection failed." );
// DB 접속이 실패했으므로 Sleep Time 을 최대로 설정한다.
SetSleepTimeMax();
return false;
}
// Query 문장 저장을 위한 버퍼 생성
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
// Query 수행 결과값 저장을 위한 변수..
// - 최종 조회된 Replication 대상 content count 정보를 저장.
int nResult = 0;
// 3. t_sms_sp_svc_product 에서 각 고객사별 Replication count 정보를 가져온다.
snprintf(szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
"SELECT sp_svc_tran_id, replication_count "
"FROM t_sms_sp_svc_product "
"WHERE replication_count > 0 "
"ORDER BY sp_svc_tran_id desc "
);
// Query 실행
rcdb.PgDoExec(szQuery);
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// RCDB t_sms_sp_svc_product 에 replication_count 컬럼이 존재하지 않는 경우
// - Replication 처리를 당장 수행할 필요가 없으므로 루프 대기처리후 다음에 다시 조회처리.
LOG( LERR, "RCDB t_sms_sp_svc_product table replication_count column not exist.[%s][%s]"
, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
// 마지막 실행시간을 현재시간으로 변경처리한 후 정상으로 반환처리한다.
// 또한 Sleep Time 역시 최대로 변경처리한다.
m_timeLastCheck = timeNow;
SetSleepTimeMax();
return true;
}
// 조회된 각 고객사별 replication 정보 확인
nResult = rcdb.GetNoTuples();
if( nResult <= 0 )
{
// Replication 을 수행할 고객사가 존재하지 않는 경우
_LOG( LINF, "ReplicateTargetSelector: service not found that content replicate.[%d]"
, nResult );
rcdb.PgClear();
rcdb.PgCloseDB();
// 마지막 실행시간을 현재시간으로 변경처리한 후 정상으로 반환처리한다.
// 또한 Sleep Time 역시 최대로 변경처리한다.
m_timeLastCheck = timeNow;
SetSleepTimeMax();
return true;
}
// Replication 을 수행할 고객사가 존재하는 경우 멤버변수에 저장
m_listCustomer.clear();
for( int i =0 ; i< nResult; i++ )
{
// map 객체이므로.. key - value 형식으로 저장 처리.
m_listCustomer[ rcdb.GetValue(i,0) ] = atoi( rcdb.GetValue(i,1));
}
// Query Resultset Clear.
rcdb.PgClear();
// 4. Replication 대상 파일을 조회한다.
// - 구형 RCDB 형상(m_bServiceTableSeparate == false) : t_dav_resouce 테이블에서 조회
// - 신형 RCDB 형상(m_bServiceTableSeparate == true ) : t_meta_tranid 테이블에서 조회
// 조회 시작시간은 이전에 저장된 m_timeLastCheck 에서 지정된 제외시간 빼고
// 조회 종료시간은 현재시간에서 지정된 제외시간만큼 뺀다.
long long timeSearchStart;
long long timeSearchEnd;
// 만약 최초 동작인 경우
if( m_timeLastCheck == 0 )
{
// 현재시간으로부터 지정된 시간만큼 뺀 시간을 조회시작시간으로 설정.
timeSearchStart = ( timeNow - GetTargetSelectIntervalOnStarting() );
}
else
{
// 이전 조회처리 시간에서 지정된 제외시간 만큼 뺀 시간을 시작시간으로 설정.
timeSearchStart = ( m_timeLastCheck - GetTargetSelectInterval() );
}
// 조회 종료시간은 현재시간에서 지정된 제외시간만큼 뺀다.
timeSearchEnd = ( timeNow - GetTargetSelectInterval() );
// NEW 2014-07-16 huibong
// 시간 동기화 관련 방어 코드 추가
// - 잘못된 시간 동기화로 인해 조회 종료시간 <= 조회 시작시간 인 경우...
// - Replication 대상 조회가 안돼 아래 Flow 의미가 없어지므로... 방어 코드 추가한다.
if( timeSearchEnd <= timeSearchStart )
{
// 에러 정보 로깅 후..
// RCDB 연결 해제 처리
// 마지막 Check Time 은
// - 시간 동기화로 오류(실제 시간보다 빨라) 인해 복제가 발생하지 않았을 수 있으므로...
// - 초기 기동 Flow 가 동작하도록 0 으로 설정 처리한다.
// Sleep Time 최대로 변경처리한다.
LOG( LERR, "Search end time[%lld] lower than start time[%lld]. check ntpdate."
, timeSearchEnd, timeSearchStart );
rcdb.PgClear();
rcdb.PgCloseDB();
// 초기 기동 Flow 가 동작하도록 설정.
m_timeLastCheck = 0;
// Sleep time 은 최대로
SetSleepTimeMax();
return false;
}
/////////////////////////////////////////////////////////////////////////////////
// NEW 2014-06-16 huibong
// - RCDB 신규 형상 재설계에 따라...Meta 정보가 서비스별 Table 단위로 분리됨.
// - 이에 기존 버전과 하위 호환성을 위해 t_dav_resource 테이블 존재 유무에 따라..
// - replication 대상 content 조회 방식을 변경한다.
// 구형 RCDB 형상인 경우.
if( m_bServiceTableSeparate == false )
{
// t_dav_resource 에서 최신 Content 목록 조회 후
// replication 대상 서비스인지 검사 후.
// Queue 에 저장 처리.
// Replication 대상 선택을 위한 Query 생성
// CHG 2014-06-19 huibong
// - 노경민 과장 요청에 의해 서브 query 상에서 uri 에 대한 DISTINCT 구문 제외
// CHG 2015-03-26 huibong
// - Cache 가 먼저 생성된 경우 cache 가 복제 대상으로 선택되는 버그 발견됨 (#21903)
// - 이를 해결하기 위해 AND is_cache = 'N' 조건절 추가함.
snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
"SELECT filename_hash, host_name, get_content_length, sp_svc_tran_id, uri "
"FROM t_dav_resource "
"WHERE uri IN "
" ( "
" SELECT uri FROM t_dav_resource "
" WHERE get_lastmodified >= %lld "
" AND get_lastmodified < %lld "
" AND deleted_yn = 'N' "
" AND is_cache !='Y' " // 캐시된 장비는 대상 선택에 영향을 미치면 안된다. 일시적인 복제증가이기 때문에 나중에 사라져버리기 때문이다.
" GROUP BY uri "
" HAVING COUNT(*) = 1 " // including is_cache ='N|R' resut=1
" ) "
" AND deleted_yn = 'N' " // 삭제된 content 는 제외.
" AND is_cache = 'N' " // 2015-03-26 #21903 cache 가 먼저 생성된 경우.. 제외 처리
" AND resource_type = 0 " // 파일만.(0:file, 1:directory)
" AND get_content_length != 0 " // 업로드 시작시 content_length=0 으로 insert된다. 즉, 0인것은 업로드중으로 간주한다.
" ORDER BY uri, get_lastmodified desc; "
, timeSearchStart, timeSearchEnd
);
_LOG( LDBG, "CReplicateSelect: select t_dav_resource, time[%lld]~[%lld]", timeSearchStart, timeSearchEnd );
// Query 실행 및 결과 확인
rcdb.PgDoExec( szQuery );
nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( nResult < 0 )
{
// RCDB t_dav_resource 테이블 조회 실패시
// Replication 처리를 당장 수행할 필요가 없으므로
// 루프 대기처리후 다음에 다시 조회처리.
LOG( LERR, "RCDB t_dav_resource replication content select failed.[%s][%s]"
, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
// 마지막 실행시간은 이전 값을 사용하도록 설정하지 않는다.
// 또한 Sleep Time 역시 최대로 변경처리한다.
SetSleepTimeMax();
return false;
}
// 결과 Row 수 확인
nResult = rcdb.GetNoTuples();
if( nResult < 0 )
{
// 오류 발생시
LOG( LERR, "RCDB t_dav_resource GetNoTuples() error[%s][%s]"
, rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
rcdb.PgCloseDB();
// 마지막 실행시간은 이전 값을 사용하도록 설정하지 않는다.
// 또한 Sleep Time 역시 최대로 변경처리한다.
SetSleepTimeMax();
return false;
}
else if( nResult == 0 )
{
// 조회 결과가 존재하지 않는 경우
_LOG( LINF, "Replicaiton target content not exist. time[%lld]~[%lld]", timeSearchStart, timeSearchEnd );
rcdb.PgClear();
rcdb.PgCloseDB();
// 마지막 실행시간을 현재시간으로 변경처리한 후 정상으로 반환처리한다.
// 또한 Sleep Time 역시 최대로 변경처리한다.
m_timeLastCheck = timeNow;
SetSleepTimeMax();
return true;
}
// 조회 결과가 존재하는 경우 조회된 결과를 Queue 에 저장
map< std::string, int >::iterator it;
CSourceData resultData;
int iPushCount = 0;
std::string strTemp;
for( int k = 0; k < nResult; k++ )
{
// 조회된 서비스 정보가 고객사 Replication List 상에 존재하는지 검사한다.
strTemp = rcdb.GetValue( k, 3 );
it = m_listCustomer.find( strTemp );
// 해당 정보를 찾은 경우
if( it != m_listCustomer.end() )
{
// 2012.09.24 : dadmain
// 복제본 생성되지 않을 문자열이 포함되어 있는지 체크한다.
string uri = rcdb.GetValue( k, 4 );
if( HasSkipKeyWord( uri ) == false )
{
resultData.m_szURI = uri;
resultData.m_szFileNameHash = rcdb.GetValue( k, 0 );
resultData.m_szOriginHostName = rcdb.GetValue( k, 1 );
resultData.m_nContentLength = atoll( rcdb.GetValue( k, 2 ) );
resultData.m_nReplicationCount = it->second;
// 2014-06-16 huibong
// 신규 RCDB 형상 지원을 위해 service sequecn (구 tran_id)정보 추가 처리.
resultData.m_szServiceSeq = strTemp;
// Queue 에 저장 처리.
PushDataToSourceQueue( resultData );
iPushCount++;
}
else
{
// skip
_LOG( LWAR, "Replication selected. but URI has SIKP_KEYWROD.[%s]", uri.c_str() );
}
}
}
_LOG( LINF, "Replication target selected. total[%d], queue insert[%d]", nResult, iPushCount );
}
else
{
// 서비스별 meta 테이블인 분리된 신형 RCDB 형상인 경우...
// - 이미 조회된 replication 대상 고객사 table 에서만 조회 처리 후
// - queue 에 저장 처리.
map< std::string, int >::iterator it;
std::string strServiceName;
// nResult 값에 따라... sleep time 이 조절되므로....
// 전체 조회 건수를 nResult 에 저장 처리해야 한다.
nResult = 0;
// 각 Query 수행 결과 저장을 위한 임시 변수. (nResult 변수 대치목적)
int dbResult;
CSourceData resultData;
int iPushCount = 0;
// Replication 대상 서비스별 Content 조회
for( it = m_listCustomer.begin(); it != m_listCustomer.end(); ++it )
{
strServiceName = it->first;
// Replication 대상 선택을 위한 Query 생성
// CHG 2014-06-19 huibong
// - 노경민 과장 요청에 의해 서브 query 상에서 uri 에 대한 DISTINCT 구문 제외
// CHG 2015-03-26 huibong
// - Cache 가 먼저 생성된 경우 cache 가 복제 대상으로 선택되는 버그 발견됨 (#21903)
// - 이를 해결하기 위해 AND is_cache = 'N' 조건절 추가함.
snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
"SELECT filename_hash, host_name, get_content_length, sp_svc_tran_id, uri "
"FROM t_meta_%s "
"WHERE uri IN "
" ( "
" SELECT uri FROM t_meta_%s "
" WHERE get_lastmodified >= %lld "
" AND get_lastmodified < %lld "
" AND deleted_yn = 'N' "
" AND is_cache !='Y' " // 캐시된 장비는 대상 선택에 영향을 미치면 안된다. 일시적인 복제증가이기 때문에 나중에 사라져버리기 때문이다.
" GROUP BY uri "
" HAVING COUNT(*) = 1 " // including is_cache ='N|R' resut=1
" ) "
" AND deleted_yn = 'N' " // 삭제된 content 는 제외.
" AND is_cache = 'N' " // 2015-03-26 #21903 cache 가 먼저 생성된 경우.. 제외 처리
" AND resource_type = 0 " // 파일만.(0:file, 1:directory)
" AND get_content_length != 0 " // 업로드 시작시 content_length=0 으로 insert된다. 즉, 0인것은 업로드중으로 간주한다.
" ORDER BY uri, get_lastmodified desc; "
, strServiceName.c_str(), strServiceName.c_str()
, timeSearchStart, timeSearchEnd
);
_LOG( LDBG, "CReplicateSelect: select t_meta_%s, time[%lld]~[%lld]", strServiceName.c_str(), timeSearchStart, timeSearchEnd );
// Query 실행 및 결과 확인
rcdb.PgDoExec( szQuery );
dbResult = rcdb.PgResult( DataBase::NOT_CLEAR );
if( dbResult < 0 )
{
// RCDB t_meta_xxx 테이블 조회 실패시
// 해당 서비스에 대해서는 Replication 처리를 당장 수행할 필요가 없다.
LOG( LERR, "RCDB t_meta_%s replication content select failed.[%s][%s]"
, strServiceName.c_str(), rcdb.GetErrorMessage().c_str(), szQuery );
rcdb.PgClear();
// loop 계속 수행.
continue;
}
// 결과 Row 수 확인
dbResult = rcdb.GetNoTuples();
if( dbResult < 0 )
{
// 오류 발생시
LOG( LERR, "RCDB t_meta_%s GetNoTuples() error[%s][%s]"
, strServiceName.c_str(), rcdb.GetErrorMessage().c_str(), szQuery );
// loop 계속 수행.
rcdb.PgClear();
continue;
}
else if( dbResult == 0 )
{
// 조회 결과가 존재하지 않는 경우
//_LOG( LDBG, "Replication target content not exist. service[%s], time[%lld]~[%lld]"
// , strServiceName.c_str(), timeSearchStart, timeSearchEnd );
// loop 계속 수행.
rcdb.PgClear();
continue;
}
// 조회 결과가 존재하는 경우 조회된 결과를 Queue 에 저장
iPushCount = 0;
for( int k = 0; k < dbResult; k++ )
{
// Skip keyword 에 포함되어 있는지 확인
string uri = rcdb.GetValue( k, 4 );
if( HasSkipKeyWord( uri ) == false )
{
resultData.m_szURI = uri;
resultData.m_szFileNameHash = rcdb.GetValue( k, 0 );
resultData.m_szOriginHostName = rcdb.GetValue( k, 1 );
resultData.m_nContentLength = atoll( rcdb.GetValue( k, 2 ) );
resultData.m_nReplicationCount = it->second;
// 2014-06-16 huibong
// 신규 RCDB 형상 지원을 위해 service sequecn (구 tran_id)정보 추가 처리.
resultData.m_szServiceSeq = strServiceName;
// Queue 에 저장 처리.
PushDataToSourceQueue( resultData );
iPushCount++;
}
else
{
// skip
_LOG( LWAR, "Replication selected. but URI has SIKP_KEYWROD.[%s][%s]"
, strServiceName.c_str(), uri.c_str() );
}
}
// 다음 Query loop 조회를 위해 RCDB 조회 결과 set 을 clear 처리
rcdb.PgClear();
// 로깅
_LOG( LINF, "Replication target selected. service[%s], total[%d], queue insert[%d]"
, strServiceName.c_str(), dbResult, iPushCount );
// 조회 결과를 합산 처리.
nResult += dbResult;
}
}
//////////////////////////////////////////////////
// RCDB 관련 정리 처리.
rcdb.PgClear();
rcdb.PgCloseDB();
// 마지막 조회시간을 현재시간으로 설정한다.
m_timeLastCheck = timeNow;
// Sleep Time 설정.
SetSleepTimeDefault();
return true;
}
// loop 간 sleep time 을 MAX 값으로 설정.
void CReplicateSelect::SetSleepTimeMax()
{
m_timeSleep = MAX_SLEEP_TIME;
return;
}
// loop 간 sleep time 을 default 값으로 설정.
void CReplicateSelect::SetSleepTimeDefault()
{
m_timeSleep = DEFAULT_SLEEP_TIME;
return;
}
// replication 대상 선택시 시간 term 설정
void CReplicateSelect::SetTargetSelectInterval( unsigned int sec )
{
if( sec > MAX_TARGET_SELECT_INTERVAL )
{
sec = MAX_TARGET_SELECT_INTERVAL;
LOG( LWAR, "conf[TARGET_SELECT_INTERVAL] value[%u] too high. use default[%d]", sec, MAX_TARGET_SELECT_INTERVAL );
}
else if( sec < MIN_TARGET_SELECT_INTERVAL )
{
sec = MIN_TARGET_SELECT_INTERVAL;
LOG( LWAR, "conf[TARGET_SELECT_INTERVAL] value[%u] too low. use default[%d]", sec, MIN_TARGET_SELECT_INTERVAL );
}
// BUG 2010-12-10 hjjang
// int 형의 곱셈은 int 결과로 나오며.. 이때 range overflow 가 발생하므로
// 아래와 같이 수정처리함.
// sec * 1000000 : micro-second 단위로 변환.
m_nTargetSelectInterval = (long long)( sec * 1000000LL );
}
// 프로세스 구동시의 replication 대상 선택 시간 term 설정
void CReplicateSelect::SetTargetSelectIntervalOnStarting( unsigned int hour )
{
if( hour < MIN_TARGET_SELECT_INTERVAL_ON_STARTING )
{
hour = MIN_TARGET_SELECT_INTERVAL_ON_STARTING;
LOG( LWAR, "conf[TARGET_SELECT_INTERVAL_ON_STARTING] value[%u] too low. use default[%d]"
, hour, MIN_TARGET_SELECT_INTERVAL_ON_STARTING );
}
// BUG 2010-12-09 huibong
// int 형의 곱셈은 int 결과로 나오며.. 이때 range overflow 가 발생하므로
// 아래와 같이 수정처리함.
// hour * 3600 * 1000000 : micro-second 단위로 변환.
m_nTargetSelectIntervalOnStarting = (long long)( hour * 3600LL * 1000000LL );
}
// micro second 단위로 설정된 replication 대상 선택 시간 term 을 반환
long long CReplicateSelect::GetTargetSelectInterval()
{
return m_nTargetSelectInterval;
}
// micro second 단위로 설정된 프로세스 구동시의 replication 대상 선택 시간 term 을 반환
long long CReplicateSelect::GetTargetSelectIntervalOnStarting()
{
return m_nTargetSelectIntervalOnStarting;
}
// 전달받은 data 를 멤버변수인 m_pSourceQueue 에 push 처리한다.
void CReplicateSelect::PushDataToSourceQueue( CSourceData& data )
{
int errorCount = 0;
while( m_pSourceQueue->Push( data ) == false )
{
if( ++errorCount == 3 )
{
LOG( LWAR, "data push into queue failed. retry. filenamehash[%s]", data.m_szFileNameHash.c_str() );
errorCount = 0;
}
sleep( 1 ); // 1 초 마다 재시도
}
return;
}
/// 2012.09.24 : dadamin
/// @brief 해당 uri에 skip keyword가 포함되어있는지 체크함
/// @param [in] uri : 복제 대상 URI(복제 대상 full 파일명)
/// @return ture 시 해당 uri에 skip keyword를 포함
bool CReplicateSelect::HasSkipKeyWord( const std::string & uri )
{
if( m_vecSipkWord.size() == 0 )
return false;
bool r = false;
for( int i = 0; i< (int)m_vecSipkWord.size(); ++i )
{
string k = m_vecSipkWord[i];
if( k.size() )
{
std::vector<string> v;
StringSplit( k, "|", v, false );
std::string::size_type pos;
pos = uri.find( v[1] );
if( pos != std::string::npos )
{
switch( v[0][0] )
{
case 'h':
{
// uri 형식이 /tran_id/path 이기 때문에
// head 위치는 2번째 '/' 위치와 동일핟.
std::string::size_type towpos = uri.find( "/", 1 );
if( towpos == pos )
{
r = true;
LOG( LDBG, "Check Skip keyword HEAD- %s:%s", uri.c_str(), v[1].c_str() );
break;
}
}
break;
case 'm':
if( pos > 0 && uri.size() > pos + v[1].size() )
{
r = true;
LOG( LDBG, "Check Skip keyword MIDDLE - %s:%s", uri.c_str(), v[1].c_str() );
break;
}
break;
case 't':
if( pos > 0 && uri.size() == pos + v[1].size() )
{
r = true;
LOG( LDBG, "Check Skip keyword TAIL - %s:%s", uri.c_str(), v[1].c_str() );
break;
}
break;
default:
r = false;
LOG( LWAR, "unknown find type. %s", uri.c_str() );
break;
}
break;
}
}
}
return r;
}
void CReplicateSelect::StringSplit( std::string str, std::string delim, std::vector<std::string> &results, bool bUseEmpty /*= false*/ )
{
const std::string strEmpty( "" );
std::string::size_type cutAt;
while( ( cutAt = str.find_first_of( delim ) ) != str.npos )
{
if( cutAt > 0 )
{
results.push_back( str.substr( 0, cutAt ) );
}
else
{
if( bUseEmpty && cutAt == 0 )
results.push_back( strEmpty );
}
str = str.substr( cutAt + 1 );
}
if( str.length() > 0 )
{
results.push_back( str );
}
}
+135
View File
@@ -0,0 +1,135 @@
/***************************************************************************
Replicate 대상 content 정보 조회 class
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __REPLICATE_SELECT_H__
#define __REPLICATE_SELECT_H__
#include <pthread.h>
#include <map>
#include "ReplicationData.h"
#include "RcdbInfo.h"
#include "DataQueue.hpp"
// CReplicateSelect
// 복제 대상 content 를 RCDB 로 부터 조회하여... Queue 에 저장 처리를 수행.
// 내부적으로 Thread 를 사용하여 주기적으로 RCDB 를 연결 후 조회 처리.
class CReplicateSelect
{
public:
// 생성자
// @param [in] pCSsourceData 조회된 Content 정보를 저장할 Queue 객체 포인트
CReplicateSelect( CDataQueue<CSourceData> * pQueue );
// 소멸자
~CReplicateSelect();
// 초기화 작업 및 DB 접속 정보를 이용하여 RCDB Test 접속을 수행함.
bool Init();
// Thread를 생성하고, 이를 시작한다.
bool Start();
// 2010년 12월 6일 장희준 추가
// Thread를 생성하고, 이를 시작한다.
// 인자로 Replication 대상 선택을 위한 초단위의 시간 설정값을 받는다.
bool Start( unsigned int interval, unsigned int intervalOnStarting );
private:
// Replication 대상 File 정보를 저장하기 위한 Queue 객체에 대한 Pointer, Not NULL
CDataQueue<CSourceData> * m_pSourceQueue;
// 내부 Thread 에 대한 ID
pthread_t m_threadHandle;
// RCDB 접속 정보를 저장할 변수.
CRcdbInfo m_rcdbInfo;
// 마지막 DB 조회 시간을 저장하기 위한 변수 ( micro-second 단위 )
long long m_timeLastCheck;
// Thread Loop 마다 Sleep Time 정보를 저장하기 위한 변수
unsigned int m_timeSleep;
// 각 고객사별 Replicatin Count 정보를 저장하기 위한 변수
// t_sms_sp_svc_product 테이블에 저장된 고객 tran_id 별 Repliation count 정보를 저장한다.
std::map< std::string, int > m_listCustomer;
// Replication 대상 선택 주기. 단위 : microsec
long long m_nTargetSelectInterval;
// 프로세스 구동시의 replication 대상 선택 주기. 단위 : microsec
long long m_nTargetSelectIntervalOnStarting;
// New 2014-06-13 huibong
// uri skip 처리 관련 설정 정보 저장을 위한 vector 객체 생성.
// 모든 content 마다 검사하는 정보이므로 처리 속도 향상을 위해 멤버변수화 처리함.
std::vector<std::string> m_vecSipkWord;
// New 2014-06-13 huibong
// RCDB 형상변경에 따라 서비스별 Table 분리 여부 판단 flag
// TODO: 향후 신규 RCDB 형상 적용 완료 후 본 항목 제거 필요.
// - false : 기존 t_dav_resource 테이블 사용.
// - ture : 신규 RCDB 형상 (서비스별 meta table 분리) 사용.
bool m_bServiceTableSeparate;
private:
// 스레드 함수
static void* threadFunc( void* arg );
// loop 단위의 replication 대상 조회 작업 수행
void Execute();
// RCDB 로 부터 Replication 대상 content 조회
bool SelectFromDB();
// replication 대상 선택시 시간 term 설정
void SetTargetSelectInterval( unsigned int sec );
// 프로세스 구동시의 replication 대상 선택 시간 term 설정
void SetTargetSelectIntervalOnStarting( unsigned int hour );
// micro second 단위로 설정된 replication 대상 선택 시간 term 을 반환
long long GetTargetSelectInterval();
// micro second 단위로 설정된 프로세스 구동시의 replication 대상 선택 시간 term 을 반환
long long GetTargetSelectIntervalOnStarting();
// 전달받은 data 를 멤버변수인 m_pSourceQueue 에 push 처리한다.
void PushDataToSourceQueue( CSourceData& data );
/// 2012.09.24 : dadamin
/// @brief 해당 uri에 skip keyword가 포함되어있는지 체크함
/// @param [in] uri : 복제 대상 URI(복제 대상 full 파일명)
/// @return ture 시 해당 uri에 skip keyword를 포함
bool HasSkipKeyWord( const std::string & uri );
void StringSplit( std::string str, std::string delim, std::vector<std::string> &results, bool bUseEmpty );
// loop 간 sleep time 을 MAX 값으로 설정.
void SetSleepTimeMax();
// loop 간 sleep time 을 default 값으로 설정.
void SetSleepTimeDefault();
};
#endif // __REPLICATE_SELECT_H__
+397
View File
@@ -0,0 +1,397 @@
#include "ReplicateThread.h"
#include <errno.h>
#include <time.h>
#include <string.h>
#include "Logger.h"
#include "FtsdSocketControl.h"
#include "ServiceConfig.h"
#include "ReportLog.h"
// ftsd 로 부터 처리 결과 수신을 위한 Total 대기 시간 정의 ( sec )
#define FHS_RESPONSE_TOTAL_TIMEOUT 3600 // ( sec, 1 hour )
// ftsd 로 부터 응답 수신 확인을 위한 socket read timeout (sec)
#define FHS_RESPONSE_CHECK_TIMEOUT 3 // 3 sec
////////////////////////////////////////////
// 생성자
CReplicateThread::CReplicateThread( CProcessStatus* const pProcessStatus
, CDataQueue<CRetryData>* const pQueueRetry
, CDataQueue<CCompleteData>* const pQueueComplete
, CRetryData& dataJob
, std::vector<std::string>& vecTargetFHS )
: m_pProcessStatus( pProcessStatus )
, m_pRetryQueue( pQueueRetry )
, m_pCompleteQueue( pQueueComplete )
, m_dataJob( dataJob )
, m_vecTargetFHS( vecTargetFHS )
{
// 멤버 변수 초기화
m_threadHandle = 0;
}
// 소멸자
CReplicateThread::~CReplicateThread()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
}
// 실제 Replication 생성 작업을 수행
bool CReplicateThread::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CReplicateThread::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// 스레드 함수
void* CReplicateThread::threadFunc( void* arg )
{
CReplicateThread* pObject = reinterpret_cast<CReplicateThread *>( arg );
pthread_detach( pthread_self() );
pthread_testcancel();
// Thread 동작시 Thread Count 1 증가 처리
pObject->m_pProcessStatus->AddReplicationCount();
// 실제 작업 수행.
pObject->Execute();
// Thread 동작 완료시 Thread Count 1 감소 처리
pObject->m_pProcessStatus->SubtractReplicationCount();
pthread_testcancel();
// NEW 2014-06-25 huibong
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
// 객체 자동 delete 처리.
delete pObject;
return NULL;
}
// 실제 Replication 생성 작업을 수행
void CReplicateThread::Execute()
{
// 1. FHS 의 ftsd 와 통신 처리를 담당할 socket control 객체 생성.
CFtsdSocketControl fhs;
// 2. 원본 Content를 가진 FHS ftsd 에 접속 수행.
bool bResult = fhs.ConnectTarget( m_dataJob.m_OriginData.m_szOriginHostName, CServiceConfig::GetInstance()->GetFhsTransferDaemonPort());
if( bResult == false )
{
// 원본 FHS 와 접속 실패시
// - 로깅 후 Retry Queue 에 저장 처리
_LOG( LERR, "CReplicateThread: source fhs[%s] connect fail.[%s]"
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
DoFailProcess();
return;
}
// 3. 연결된 원본 FHS 로 replication 생성 요청을 전달
bResult = fhs.SendFileReplicationRequest( m_dataJob.m_OriginData.m_szFileNameHash
, m_dataJob.m_OriginData.m_nContentLength
, CServiceConfig::GetInstance()->GetHashCheckLevel()
, true
, m_vecTargetFHS );
if( bResult == false )
{
// replication 요청 전달 실패시
// - 로깅 후 Retry Queue 에 저장 처리
_LOG( LERR, "CReplicateThread: replication request send fail.[%s][%s]"
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
DoFailProcess();
return;
}
// 4. FHS 로 부터 처리 결과를 수신할때 까지 대기
// - 이상 상황 발생시 무한 대기 가능성이 존재하므로..
// 미리 정의된 FHS_RESPONSE_TOTAL_TIMEOUT 까지만 대기하도록 한다.
// response timeout 처리를 위한 변수
time_t timeStart;
time_t timeNow;
// 응답 결과를 저장하기 위한 변수
int nResult;
bool bSuccess = false;
std::string szErrorMessage;
// 대기 시작 전 현재시간을 구한다.
timeStart = time( &timeStart );
// FHS 로 부터 응답 수신 대기
while( 1 )
{
// FHS 로 부터 응답 수신 처리
// -1 : 연결 종료
// 0 : Timeout 발생
// 1 : 응답 수신
// 2 : 복제 관련 응답이 아닌 다른 응답인 경우.. (발생하지 않지만.. 혹시나)
nResult = fhs.GetFileReplicationResult( FHS_RESPONSE_CHECK_TIMEOUT
, bSuccess
, szErrorMessage
, m_mapSuccessFhs
, m_mapFailFhs );
if( nResult == 0 )
{
// 지정된 시간동안 응답 결과를 수신하지 못한 경우
// - FHS 와 연결이 정상인지 Alive 체크 호출
// - 만약 연결이 끝어진 경우.. 다음 loop 에서 -1 연결 종료가 리턴됨.
if( fhs.SendAliveCheck() == false )
{
_LOG( LERR, "CReplicateThread: alive check fail.[%s][%s]"
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
}
// Timeout 이 발생했으므로 바로 재시도하지 않고 조금 대기 후 다시 시도.
sleep( 1 );
}
else if( nResult == 1 )
{
// FHS 로 부터 응답을 수신한 경우.
// - Content 이상으로 인해 전송 자체를 하지 못했는지 여부 판단
if( bSuccess == true )
{
// 정상적으로 Replication 이 처리된 경우..
_LOG( LDBG, "CReplicateThread: result receive.[%s][%s]"
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
DoSuccessProcess();
}
else
{
// Content 이상으로 인해 Replication 자체에 문제가 있는 경우.
_LOG( LERR, "CReplicateThread: error result receive.[%s][%s][%s][%llu]"
, szErrorMessage.c_str()
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str()
, m_dataJob.m_OriginData.m_nContentLength );
DoErrorProcess( szErrorMessage );
}
// 처리 결과를 수신했으므로 loop 종료.
break;
}
else if( nResult == -1 )
{
// FHS 와 연결이 끝어진 경우.
// - retry queue 에 저장하고 본 루프는 종료
_LOG( LERR, "CReplicateThread: fhs connection broken.[%s][%s]"
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
DoFailProcess();
break;
}
else if( nResult == 2 )
{
// Replication 관련 응답이 아닌 경우
// - 무시
;
}
else
{
// 기타 정의되지 않은 결과값 반환시
// - FHS 와 세션 이상이 발생할 수 있으므로
// - 실패로 처리하여 retry queue 에 저장하고 본 루프는 종료
_LOG( LERR, "CReplicateThread: receive undefine result.[%d][%s][%s]"
, nResult
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
DoFailProcess();
break;
}
// Total Reponse 대기 시간 확인
if( difftime( time( &timeNow ), timeStart ) > FHS_RESPONSE_TOTAL_TIMEOUT )
{
// Total Response Timeout 발생시
// - 로깅 후 Retry Queue 에 저장 처리
// - 본 대기 루프를 종료
_LOG( LERR, "CReplicateThread: replication result receive timeout.[%s][%s]"
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
DoFailProcess();
break;
}
} // while( 1 )
}
// Replication 처리 관련 오류가 발생한 경우 처리 함수.
// - 재시도 횟수를 확인하여
// - Retry queue 에 저장 처리
void CReplicateThread::DoFailProcess()
{
// 재시도 횟수 초과 여부 확인.
if( m_dataJob.m_uFailCount + 1 > CServiceConfig::GetInstance()->GetMaxRetryCount() )
{
// 재시도 횟수 초과시
// - Report 로그에 기록하고 Retry Queue 에는 저장하지 않는다.
_LOG( LERR, "CReplicateThread: replicate thread fail and retry count over.[%s][%s]"
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
CReportLog reportLog;
if( reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
{
std::string strErrorMessage;
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
}
else
{
reportLog.Write( "ERR", "[%s][Replication fail. check content[%s][%s]]"
, PROG_NAME
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str() );
}
}
else
{
// Retry Queue 에 저장 처리.
m_dataJob.m_uFailCount += 1;
m_dataJob.m_timeFailed = time( 0 );
// Retry Queue 가 Full 인 상태에서
// 작업 Thread 가 모두 Push 상태인 경우 무한 Block 상태 발생 가능...
// 하지만.. 실제 발생 가능이 희박하므로... 무한 재시도 처리.
while( m_pRetryQueue->Push( m_dataJob ) == false )
{
sleep( 1 );
}
}
return;
}
// Replication 처리에 대해 FHS 에서 실패로 응답한 경우.
// - 대부분 Replication 대상 content 가 존재하지 않거나 Size 변경 등이 발생한 경우임.
// - 따라서 해당 Content 에 대해서는 Retry 하지 않고.. Report 로그에만 기록하고 끝낸다.
void CReplicateThread::DoErrorProcess( string& szErrorMessage )
{
// 2017-11-15 CHG huibong 대량 report 로그 발생 관련 항목 조정 (#31526)
// - 토토디스크 서비스에서 업로드에 따른 size mismatch 대량 report 로그 관련
// - error message 가 "source file size not match" 인 경우 report 로그에 기록되지 않도록 수정한다.
// - 실제 error message 내용은 "source file size not match.[ request: 877314616, local: 1137364384 ]" 형식이므로
// - 앞의 일부분만 비교 처리.
if(strncmp(szErrorMessage.c_str(), "source file size not match", 26) == 0 )
{
return;
}
else
{
CReportLog reportLog;
if( reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
{
std::string strErrorMessage;
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
}
else
{
reportLog.Write( "ERR", "[%s][Replication fail. check content[%s][%s][%llu][%s]]"
, PROG_NAME
, m_dataJob.m_OriginData.m_szOriginHostName.c_str()
, m_dataJob.m_OriginData.m_szURI.c_str()
, m_dataJob.m_OriginData.m_nContentLength
, szErrorMessage.c_str() );
}
return;
}
}
// Replication 처리에 대해 FHS 에서 성공을 응답한 경우 처리함수
// - 성공된 FHS 와 실패된 FHS 정보를 동시에 전달하므로 확인하여 처리해야 한다.
void CReplicateThread::DoSuccessProcess()
{
// 멤버 변수에 저장된 정보를 처리하기 위한 iterator
std::map<std::string, std::string>::iterator itSuccessFhs;
// m_mapSuccessFhs 에 저장된 성공 결과에 대한 처리
// - success fhs host, hash_name 의 정보 저장된 상태
// - Complete Queue 에 저장.
for( itSuccessFhs = m_mapSuccessFhs.begin(); itSuccessFhs != m_mapSuccessFhs.end(); ++itSuccessFhs )
{
// complete queue 에 저장하기 위한 객체 조립
CCompleteData completeResult;
completeResult.m_OriginData = m_dataJob.m_OriginData;
completeResult.m_szTargetHostName = itSuccessFhs->first;
completeResult.m_szTargetFileNameHash = itSuccessFhs->second;
// complete queue 에 저장 처리
// - complete queue 가 full 상태일 수 있어 push 실패 가능성 존재
// - 하지만... complete queue 에 저장 못할 경우...
// FHS 에 생성된 replication 은 처치 곤란한 쓰레기가 될 수 있으므로....
// 최대한 push 처리한다.
while( m_pCompleteQueue->Push( completeResult ) == false )
{
// push 실패시 잠시 대기 후 재시도
sleep( 1 );
}
// 원본 Data 객체에 성공한 FHS 정보 입력 처리
// - 일부 FHS 에만 복제 성공하고... 다른 FHS 복제는 실패한 경우...
// - Retry 를 통해 복제가 재시도 되도록 하기 위함.
m_dataJob.m_listSuccessHost.push_back( completeResult.m_szTargetHostName );
}
// m_mapFailFhs 에 저장된 실패 결과에 대한 처리
// - 재시도 횟수 초과시 Report 로그 기록 후 버림
// - 재시도 횟수 이하인 경우 fail count 증가 후 Retry Queue 에 저장
if( m_mapFailFhs.empty() == false )
{
// 멤버 변수에 저장된 정보를 처리하기 위한 iterator
// - failed fhs host, error message
std::map<std::string, std::string>::iterator itFailFhs;
// 실패 내역에 대한 로깅
for( itFailFhs = m_mapFailFhs.begin(); itFailFhs != m_mapFailFhs.end(); ++itFailFhs )
{
_LOG( LWAR, "CReplicateThread: replicate fail [%s]->[%s][%s]"
, m_dataJob.m_OriginData.m_szURI.c_str()
, itFailFhs->first.c_str()
, itFailFhs->second.c_str() );
}
// 재시도 횟수 확인 처리 및 Retry queue 관련 처리
DoFailProcess();
}
return;
}
+108
View File
@@ -0,0 +1,108 @@
/***************************************************************************
Content 에 대한 실제 Replication 처리를 수행하는 Thread class
-----------------------------------------
begin : 2010/03/13
copyright : (C) 2010 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.3
CopyRight(C) 2010 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 __REPLICATE_THREAD_H__
#define __REPLICATE_THREAD_H__
#include <vector>
#include <map>
#include <string>
#include <pthread.h>
#include "ProcessStatus.h"
#include "DataQueue.hpp"
#include "ReplicationData.h"
// CReplicateThread
// 전달받은 Content 에 대해 각 FHS 와 통신을 통해 실제 Replication 생성을 수행
// - Replicate 정보 및 FHS 정보를 SocketControl 을 이용하여 ftsd 로 전송
// - 처리 결과 수신 후 CompletQueue 또는 RetryQueue 상에 저장처리
// 내부적으로 Thread를 사용
class CReplicateThread
{
public:
// 생성자
CReplicateThread( CProcessStatus* const pProcessStatus
, CDataQueue<CRetryData>* const pQueueRetry
, CDataQueue<CCompleteData>* const pQueueComplete
, CRetryData& dataJob
, std::vector<std::string>& vecTargetFHS );
// 소멸자
~CReplicateThread();
// thread를 생성하여 업무 flow 를 시작.
bool Start();
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 실제 Replication 생성 작업을 수행
void Execute();
// Replication 처리 관련 오류가 발생한 경우 처리 함수.
// - 재시도 횟수를 확인하여
// - Retry queue 에 저장 처리
void DoFailProcess();
// Replication 처리에 대해 FHS 에서 실패로 응답한 경우.
// - 대부분 Replication 대상 content 가 존재하지 않거나 Size 변경 등이 발생한 경우임.
// - 따라서 해당 Content 에 대해서는 Retry 하지 않고.. Report 로그에만 기록하고 끝낸다.
void DoErrorProcess( std::string& szErrorMessage );
// Replication 처리에 대해 FHS 에서 성공을 응답한 경우 처리함수
// - 성공된 FHS 와 실패된 FHS 정보를 동시에 전달하므로 확인하여 처리해야 한다.
void DoSuccessProcess();
private:
// 쓰레드 핸들
pthread_t m_threadHandle;
// 프로세스 상의 Queue, Thread 상태를 관리하는 CProcessStatus 객체에 대한 포인터
// - 생성자를 통해 전달받음
CProcessStatus * m_pProcessStatus;
// 복제 처리 관련 재시도 Content 정보를 관리하는 Queue 객체에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CRetryData> * m_pRetryQueue;
// 복제 완료된 Content 정보를 관리하는 Queue 객체에 대한 포인터
// - 생성자를 통해 전달받음
CDataQueue<CCompleteData> * m_pCompleteQueue;
// 복제 대상 Content 정보 저장 객체
// - 생성자를 통해 전달받음
CRetryData m_dataJob;
// 복제본을 저장할 Proper FHS 저장 객체
// - 생성자를 통해 전달받음
std::vector<std::string> m_vecTargetFHS;
// ftsd 로 부터 정상 처리된 결과 정보를 수신하기 위한 멤버 객체
// - success fhs host, hash_name
std::map<std::string, std::string> m_mapSuccessFhs;
// ftsd 로 부터 실패 처리된 결과 정보를 수신하기 위한 멤버 객체
// - failed fhs host, error message
std::map<std::string, std::string> m_mapFailFhs;
};
#endif // __REPLICATE_THREAD_H__
+84
View File
@@ -0,0 +1,84 @@
/***************************************************************************
Replication 관련 Data 객체 정의
-----------------------------------------
begin : 2010/03/09
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.3
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 __REPLICATION_DATA_H__
#define __REPLICATION_DATA_H__
#include <list>
#include <string>
#include <time.h>
// CSourceData class
class CSourceData
{
public :
// RCDB 의 URI 정보
std::string m_szURI;
// RCDB의 filename_hash 정보
std::string m_szFileNameHash;
// RCDB의 hostname 정보
std::string m_szOriginHostName;
// NEW 2014-06-16
// 서비스별 RCDB table 분리 (RCDB 신규 형상)에 따라...
// Query 처리가 간편하도록 서비스 정보 필드 추가 (RCDB sp_svc_tran_id, CCDB svc_seq )
std::string m_szServiceSeq;
// 파일의 크기. RCDB의 get_content_length
unsigned long long m_nContentLength;
// Replication 해야할 갯수
int m_nReplicationCount;
};
// CRetryData class
class CRetryData
{
public :
// 실패 개수
unsigned int m_uFailCount;
// 실패 시각(default=now)
time_t m_timeFailed;
// 성공한 FHS 정보
std::list<std::string> m_listSuccessHost;
// 작업 시도 대상(source 정보)
CSourceData m_OriginData;
// 생성자. 현재 시각으로 time설정
inline CRetryData() { time(&m_timeFailed); }
};
// CCompleteData class
class CCompleteData
{
public :
// content 가 생성된 장비 host name
std::string m_szTargetHostName;
// 생성된 content 의 filename_hash 정보
std::string m_szTargetFileNameHash;
// 원본 정보
CSourceData m_OriginData;
};
#endif // __REPLICATION_DATA_H__
+440
View File
@@ -0,0 +1,440 @@
#include "ServiceConfig.h"
#include <stdlib.h>
#include "Logger.h"
// static 변수 초기화
CServiceConfig * CServiceConfig::m_pInstance = NULL;
// 생성자
CServiceConfig::CServiceConfig()
{
// default 값 설정.
// 지정된 conf 파일에 해당 설정이 없어도 동작되도록 처리할 값에 대해서만 default 값을 정의한다.
}
// 소멸자
CServiceConfig::~CServiceConfig()
{
// 객체 소멸시...
// 만약 static GetInstance() 함수가 가르키는 객체가 자기 자신이라면...
// 소멸 처리에 의해 문제가 발생할 수 있으므로... 이에 대한 처리를 해 준다.
if( CServiceConfig::GetInstance() == this )
{
CServiceConfig::m_pInstance = NULL;
}
}
bool CServiceConfig::Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
{
// 1. 임시 처리용 Instance 를 생성한다.
CServiceConfig * pTempInstance = new CServiceConfig();
// 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( CServiceConfig::GetInstance() == NULL )
{
CServiceConfig::m_pInstance = pTempInstance;
}
else
{
// Instance replace...
CServiceConfig * pPreviosInstance = CServiceConfig::m_pInstance;
CServiceConfig::m_pInstance = pTempInstance;
delete pPreviosInstance;
}
return true;
}
CServiceConfig * CServiceConfig::GetInstance( )
{
return CServiceConfig::m_pInstance;
}
bool CServiceConfig::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;
int nValue;
std::vector< std::string > vecValue;
// Log path
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
{
nValue = atoi(strValue.c_str());
if( nValue < 0 || nValue > MAX_LOG_LEVEL )
{
strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
return false;
}
else
m_nLogLevel = nValue;
strValue.clear();
}
// RC ID
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
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 || nValue > 65535 )
{
strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
return false;
}
else
m_nRcdbPort = nValue;
strValue.clear();
}
// RCDB DB NAME
if( GetConfigValue( conf, strProgramName, "RCDB_DB_NAME", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbName = strValue;
strValue.clear();
}
// RCDB ACCOUNT
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbAcct = strValue;
strValue.clear();
}
// RCDB ACCOUNT PASSWORD
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT_PW", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbAcctPw = strValue;
strValue.clear();
}
// FHS FTSD (File TranSfer Daemon) Service Port
if( GetConfigValue( conf, strProgramName, "FHS_TRANSFER_DAEMON_PORT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 || nValue > 65535 )
{
strErrorMessage = "Config [FHS_TRANSFER_DAEMON_PORT] value[" + strValue + "] is not valid";
return false;
}
else
m_nFhsTransferDaemonPort = nValue;
strValue.clear();
}
// Max Queue Count(size) limit ( 0: infinite )
if( GetConfigValue( conf, strProgramName, "MAX_QUEUE_COUNT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [MAX_QUEUE_COUNT] value[" + strValue + "] is not valid";
return false;
}
else
m_nMaxQueueSize = nValue;
strValue.clear();
}
// Max Retry Count if file processing failed.
if( GetConfigValue( conf, strProgramName, "MAX_RETRY_COUNT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [MAX_RETRY_COUNT] value[" + strValue + "] is not valid";
return false;
}
else
m_nMaxRetryCount = nValue;
strValue.clear();
}
// File Hash Check Level: 0:Not use hash, 1:Partitial hash(Default), 2:Full Hash
if( GetConfigValue( conf, strProgramName, "FILE_HASH_CHECK_LEVEL", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 || nValue > 2)
{
strErrorMessage = "Config [FILE_HASH_CHECK_LEVEL] value[" + strValue + "] is not valid";
return false;
}
else
m_nHashCheckLevel = nValue;
strValue.clear();
}
// Max count of file replication request thread
if( GetConfigValue( conf, strProgramName, "MAX_REPLICATION_THREAD", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 )
{
strErrorMessage = "Config [MAX_REPLICATION_THREAD] value[" + strValue + "] is not valid";
return false;
}
else
m_nMaxReplicationThread = nValue;
strValue.clear();
}
// FHS Hot contents 정보 수신을 위한 TCP Listen Port
if( GetConfigValue( conf, strProgramName, "FHS_HOTCONTENT_LISTEN_TCP_PORT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 || nValue > 65535 )
{
strErrorMessage = "Config [FHS_HOTCONTENT_LISTEN_TCP_PORT] value[" + strValue + "] is not valid";
return false;
}
else
m_nFhsHotContentListenPort = nValue;
strValue.clear();
}
// Hot content cache 생성을 위한 최소 동접 fd 설정값.
if( GetConfigValue( conf, strProgramName, "HOTCONTENT_CREATE_MINIMUM_FD_COUNT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi( strValue.c_str() );
if( nValue < 0 )
{
strErrorMessage = "Config [HOTCONTENT_CREATE_MINIMUM_FD_COUNT] value[" + strValue + "] is not valid";
return false;
}
else
m_nCacheCreateMinimumFdCount = nValue;
strValue.clear();
}
// Cache File Timeout (sec) (Default: 24Hour)
if( GetConfigValue( conf, strProgramName, "CACHE_FILE_TIMEOUT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 )
{
strErrorMessage = "Config [CACHE_FILE_TIMEOUT] value[" + strValue + "] is not valid";
return false;
}
else
m_nCacheFileTimeout = nValue;
strValue.clear();
}
// Max count of file cache request thread
if( GetConfigValue( conf, strProgramName, "MAX_CACHE_THREAD", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 )
{
strErrorMessage = "Config [MAX_CACHE_THREAD] value[" + strValue + "] is not valid";
return false;
}
else
m_nMaxCacheThread = nValue;
strValue.clear();
}
// Target select interval on running
if( GetConfigValue( conf, strProgramName, "TARGET_SELECT_INTERVAL", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [TARGET_SELECT_INTERVAL] value[" + strValue + "] is not valid";
return false;
}
else
m_nTargetSelectInterval = nValue;
strValue.clear();
}
// Target select interval on starting
if( GetConfigValue( conf, strProgramName, "TARGET_SELECT_INTERVAL_ON_STARTING", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [TARGET_SELECT_INTERVAL_ON_STARTING] value[" + strValue + "] is not valid";
return false;
}
else
m_nTargetSelectIntervalOnStarting = nValue;
strValue.clear();
}
// Skip keyword : h|path (head) or m|path(middle) or t|path(tail)
// - 본 설정갑은 없어도 동작하도록 처리한다.
// CHG 2014-09-02 huibong
// program -> common section 변경에 따른 기능 추가 처리 ( #20201 )
if( conf.GetConfig( strProgramName, "SKIP_KEYWORD", vecValue ) == true )
{
// 해당 값이 존재할 경우..
// Multi value 이므로 갯수 검사
if( vecValue.size() > 0 )
{
m_vecSkipWord = vecValue;
}
vecValue.clear();
}
else
{
// program section 에 존재하지 않을 경우... COMMON 세션 확인
if( conf.GetConfig( "COMMON", "SKIP_KEYWORD", vecValue ) == true )
{
// 해당 값이 존재할 경우..
// Multi value 이므로 갯수 검사
if( vecValue.size() > 0 )
{
m_vecSkipWord = vecValue;
}
vecValue.clear();
}
}
// conf 정보 로딩이 모두 정상적으로 완료된 경우...
// 마지막으로 전달받은 config 명, section 정보를 저장한다.
m_strConfigFileName = strConfFileName;
m_strProgramName = strProgramName;
return true;
}
bool CServiceConfig::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;
}
+163
View File
@@ -0,0 +1,163 @@
/****************************************************************************
rc_rmcd's service config module
-----------------------------------------
begin : 2014/05/19
copyright : (C) 2005 Solbox Inc.
author : Dev 1 Team
- huibong : 최초 작성
email : dev1@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.
*****************************************************************************/
#ifndef __SERVICE_CONFIG_H__
#define __SERVICE_CONFIG_H__
#include <string>
#include "Config.h"
// 본 모듈은 conf 파일의 정보를 read 하여... 본 프로세스 관련 설정 정보를 저장한다.
// 본 모듈은 singleton 으로 동작시켜 Main 프로세스 및 Worker 프로세스에서도 접근이 가능토록 한다.
// conf 파일에 대한 파싱 및 설정 정보 추출은 lib 하위의 Config 클래스를 이용한다.
// 본 객체는 singleton 객체이므로 상속받아 사용하지 않도록 한다. ( 상속받아 사용할 수 없도록 private 처리함 )
class CServiceConfig
{
private:
// sigleton 객체 생성을 위해 생성자를 public 으로 처리하지 않음.
CServiceConfig();
// 소멸자
virtual ~CServiceConfig();
static CServiceConfig * 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 CServiceConfig * 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
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.
// Process 공통
unsigned int m_nHashCheckLevel; // Content 복제 및 Cache 관련 전송시 Hash Check Level 설정.
// 0:Not use hash, 1:Partitial hash(Default), 2:Full Hash
unsigned int m_nFhsTransferDaemonPort; // FHS File Transfer Daemon (ftsd) 의 TCP Listen Port = 14001 (default)
// Replication 관련 설정 정보
unsigned int m_nMaxQueueSize; // Max Queue Size ( 0: infinite, 4096: default )
unsigned int m_nMaxRetryCount; // 복제 실패 발생시 최대 재시도 횟수, ( default 2 )
unsigned int m_nMaxReplicationThread; // 동시 수행 Replication Thread 의 최대 갯수
unsigned int m_nTargetSelectInterval; // 현재 시간을 기준으로 지정된 시간 이전의 Content 에 대해서만 replication 대상 여부를 조회
// 금방 업로드된 Content 의 경우.. 수정, 삭제 등의 추가 작업 발생이 가능하므로...
// 되도록 추가 작업이 완료된 Cotnent 에 대해서 Replication 이 수행될 수 있도록
// 지정된 시간동안 Delay 처리, 이로 인해 지정된 시간만큼 지연 복제가 발생된다.
// range : 180 ~ 1800 (second)
unsigned int m_nTargetSelectIntervalOnStarting; // 최초 기동시 Replication 대상 조회 범위를 지정
// 본 설정값만큼 과거 Content 중에서 Replicaton 대상을 조회한다.
// 본 설정값은 기동시 1번만 적용된다.
// range : 1 ~ unlimited (hour) (Default: 168Hour(7Days))
std::vector<std::string> m_vecSkipWord; // 복제 대상에서 제외할 문자열 정보 저장.
// 설정값이 존재하지 않을 수 있음.
// Hot Content Cache 관련 설정 정보
unsigned int m_nFhsHotContentListenPort; // FHS fimngd 에서 전송하는 Hot Content 관련 TCP Data 수신을 위한 TCP Port = 13110
unsigned int m_nCacheCreateMinimumFdCount; // Hot content cache 생성을 위한 최소 동접 fd 설정값.
// - fimngd 로 부터 전송받은 정보 중 본 설정값 이상인 Content 에 대해서만 Cache 생성을 시도한다.
unsigned int m_nCacheFileTimeout; // Cache 목적으로 생성된 Content 의 최대 생존 시간 (sec, default 84600 = 24hour )
unsigned int m_nMaxCacheThread; // 동시 수행 Cache 생성 Thread 의 최대 갯수
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 GetHashCheckLevel() { return m_nHashCheckLevel; }
unsigned int GetFhsTransferDaemonPort() { return m_nFhsTransferDaemonPort; }
unsigned int GetMaxQueueSize() { return m_nMaxQueueSize; }
unsigned int GetMaxRetryCount() { return m_nMaxRetryCount; }
unsigned int GetMaxReplicationThread() { return m_nMaxReplicationThread; }
unsigned int GetTargetSelectInterval() { return m_nTargetSelectInterval; }
unsigned int GetTargetSelectIntervalOnStarting() { return m_nTargetSelectIntervalOnStarting; }
std::vector<std::string> GetSkipWord() { return m_vecSkipWord; }
unsigned int GetFhsHotContentListenPort() { return m_nFhsHotContentListenPort; }
unsigned int GetHotContentCreateMinimumFd() { return m_nCacheCreateMinimumFdCount; }
unsigned int GetMaxCacheThread() { return m_nMaxCacheThread; }
unsigned int GetCacheFileTimeout() { return m_nCacheFileTimeout; }
};
#endif /* __SERVICE_CONFIG_H__ */
+40
View File
@@ -0,0 +1,40 @@
#include "Util.h"
#include <sys/time.h>
#define SEC_TO_MICROSEC (1000000LL)
/// @brief 현재 시간 정보를 micro-second 단위로 계산하여 반환
/// @return 1970년 1월 1일 이후부터의 현재시간을 micro-second(1/1,000,000) 단위로 반환처리, 오류발생시에는 -1 값을 반환.
long long longtime_now()
{
struct timeval tv;
if( gettimeofday(&tv, NULL) == 0 )
{
// 성공시 micro-second 단위의 값을 반환
return tv.tv_sec * SEC_TO_MICROSEC + tv.tv_usec;
}
else
{
// 실패시 -1 값을 반환.
return -1;
}
}
/// @brief source string에 주어진 search string을 replacement로 변환 화여 반환
std::string stringreplace(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;
}
+44
View File
@@ -0,0 +1,44 @@
/***************************************************************************
Util functions
-----------------------------------------
begin : 2010/03/13
copyright : (C) 2010 SolutionBox Inc.
author : Tae-il, KIm
email : tael@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.
***************************************************************************/
/* 수정일 /수정자 : 수정 내역
* 2010-03-31 / huibong : gettimeofday() 함수 반환값에 대한 처리 추가, C++ Style extern 선언 추가.
*/
#ifndef __UTIL_H__
#define __UTIL_H__
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
/// @brief 현재 시간 정보를 micro-second 단위로 계산하여 반환.
/// @return 1970년 1월 1일 이후부터의 현재시간을 micro-second(1/1,000,000) 단위로 반환처리, 오류발생시에는 -1 값을 반환.
long long longtime_now(void);
/// @brief source string에 주어진 search string을 replacement로 변환 화여 반환
std::string stringreplace(const std::string& source, const std::string search, const std::string replacement);
#ifdef __cplusplus
}
#endif
#endif // __UTIL_H__
+586
View File
@@ -0,0 +1,586 @@
#include "Worker.h"
#include "ProcessRename.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include "DataQueue.hpp"
#include "ReplicationData.h"
#include "CacheRequestData.h"
#include "CacheData.h"
#include "ProcessStatus.h"
#include "ReplicateSelect.h"
#include "ReplicateInsert.h"
#include "ProperFhs.h"
#include "ReplicateControl.h"
#include "FimngdSocketControl.h"
#include "CacheJobList.h"
#include "CacheDelete.h"
#include "CacheInsert.h"
#include "CacheOrder.h"
#include "CacheControl.h"
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
// hot content 정보 관련 fimngd 로 부터 TCP 접속 요청 수신시
// 이를 처리할 Thread 에게 전달할 정보 구조체
struct stFhsReceiveThreadArg {
// client socket descriptor
int clientSocketDesc;
// Client 의 IP 주소 정보를 저장한 변수
char szClientAddress[INET_ADDRSTRLEN];
// 요청된 Cache 정보를 저장하기 위한 Queue 에 대한 포인터.
CDataQueue<CCacheRequestData> * pQueueCacheRequest;
};
/// @brief Worker Process 의 main 함수
int WorkerMain( int socketListenHotContent )
{
// Process Rename
set_ps_display(PROG_NAME": Worker [initialize process]", false);
// Set Signal Handler
SetSignalWorker();
// 로그 출력 순서가 Main 프로세스와 꼬여서... 1초간 대기 후 진행
sleep( 1 );
// 1. 각 모듈 생성 및 초기화.
// - 초기화 실패시 EXIT_FAILURE 를 return 하여 프로세스가 종료되도록 한다.
// 1.1 Data 처리를 위한 객체 생성
// 복제대상 정보를 관리하는 큐.
CDataQueue<CSourceData> queueReplicate;
queueReplicate.SetMaxSize( CServiceConfig::GetInstance()->GetMaxQueueSize() );
// 복제 시도 후 실패한 정보를 관리하는 큐.
CDataQueue<CRetryData> queueRetry;
queueRetry.SetMaxSize( CServiceConfig::GetInstance()->GetMaxQueueSize() );
// 복제 완료된 결과를 관리 하는 큐.
CDataQueue<CCompleteData> queueComplete;
queueComplete.SetMaxSize( CServiceConfig::GetInstance()->GetMaxQueueSize() );
// Cache 생성 요청 정보 관리 queue
CDataQueue<CCacheRequestData> queueCacheRequest;
queueCacheRequest.SetMaxSize( CServiceConfig::GetInstance()->GetMaxQueueSize() );
// Cache 생성 완료 결과 관리 queue
CDataQueue<CCacheData> queueCacheComplete;
queueCacheComplete.SetMaxSize( CServiceConfig::GetInstance()->GetMaxQueueSize() );
// Cache 생성 관련 job 정보 관리 객체 생성
CCacheJobList objCacheJob;
// 1.2 공통 모듈 생성
// 모니터링 및 전체 Thread 갯수 제어 관련 객체 생성
CProcessStatus objProcessStatus( &queueReplicate, &queueRetry, &queueComplete, &queueCacheRequest, &queueCacheComplete, &objCacheJob );
if( objProcessStatus.Init( CServiceConfig::GetInstance()->GetMaxReplicationThread()
, CServiceConfig::GetInstance()->GetMaxCacheThread() ) == false )
{
LOG( LERR, "Worker[%d] process status module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// RCDB 에서 proper FHS 정보를 조회하는 Thread 모듈 생성.
CProperFhs objProperFhs;
if( objProperFhs.Init() == false )
{
LOG( LERR, "Worker[%d] proper fhs management module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// 1.3 Replication 관련 모듈 생성
// replication 에 완료된 content 를 RCDB 에 저장 처리하는 thread 모듈 생성
CReplicateInsert objReplicateInsert( &queueComplete );
if( objReplicateInsert.Init() == false )
{
LOG( LERR, "Worker[%d] replication complete insert module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// RCDB 상에서 replication 대상 content 조회 Thread 모듈 생성
CReplicateSelect objReplicateSelect( &queueReplicate );
if( objReplicateSelect.Init() == false )
{
LOG( LERR, "Worker[%d] replication select module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// Replication Control Thread 모듈 생성
CReplicateControl objReplicateControl( &objProperFhs, &objProcessStatus
, &queueReplicate, &queueRetry, &queueComplete );
if( objReplicateControl.Init() == false )
{
LOG( LERR, "Worker[%d] replication control module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// 1.4 Hot Content Cache 관련 모듈 생성
// RCDB 에서 유효기간이 만료된 Cache Content 삭제 Thread 모듈 생성.
CCacheDelete objCacheDelete;
if( objCacheDelete.Init() == false )
{
LOG( LERR, "Worker[%d] cache delete module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// 생성된 Cache 완료 정보를 RCDB 에 저장하는 Thread 모듈 생성.
CCacheInsert objCacheInsert( &queueCacheComplete, &objCacheJob );
if( objCacheInsert.Init() == false )
{
LOG( LERR, "Worker[%d] cache complete insert module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// Cache 요청을 확인하여 Job 에 등록을 수행하는 Thread 모듈 생성
CCacheOrder objCacheOrder( &queueCacheRequest, &objCacheJob, &objProperFhs );
if( objCacheOrder.Init() == false )
{
LOG( LERR, "Worker[%d] cache job order module Init() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// Cache 생성 처리를 담당하는 Thread 제어 모듈 생성
CCacheControl objCacheControl( &objProcessStatus, &objCacheJob, &queueCacheComplete );
// 2. 각 수행 모듈 기동 처리.
// - 기동 실패시 exit( EXIT_FAILURE ) 또는 return EXIT_FAILURE 를 수행하여
// 초기화 실패로 인한 프로세스 종료가 발생토록 한다.
// 2.1 공통 모듈 기동
// Process 상태 표시 관련 Thread 기동
if( objProcessStatus.Start() == false )
{
LOG( LERR, "Worker[%d]: Process status module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CProcessStatus module start.", getpid() );
// RCDB 에서 Proper FHS 내역 조회 및 관리 관련 모듈 기동처리.
if( objProperFhs.Start() == false )
{
LOG( LERR, "Worker[%d]: Proper FHS management module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CProperFhs module start.", getpid() );
// 2.2 Replication 관련 수행 모듈 기동
// Replication 완료 content 에 대한 RCDB insert 처리 모듈 기동처리.
if( objReplicateInsert.Start() == false )
{
LOG( LERR, "Worker[%d]: Replicate complete insert module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CCompleteTargetInserter module start.", getpid() );
// Replication 대상 content 조회 모듈 기동처리.
if( objReplicateSelect.Start( CServiceConfig::GetInstance()->GetTargetSelectInterval()
, CServiceConfig::GetInstance()->GetTargetSelectIntervalOnStarting() ) == false )
{
LOG( LERR, "Worker[%d]: Replicate target selector module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CReplicateTargetSelector module start.", getpid() );
// Replication 생성 관련 제어 모듈 기동처리.
if( objReplicateControl.Start() == false )
{
LOG( LERR, "Worker[%d]: Replication control module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CReplicateControl module start.", getpid() );
// 2.3 Hot content cache 관련 수행 모듈 기동
// RCDB 에서 유효기간 만료된 Cache Content 삭제 처리 모듈 기동처리.
if( objCacheDelete.Start() == false )
{
LOG( LERR, "Worker[%d]: Cache delete module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CCacheDelete module start.", getpid() );
// 생성된 Cache 정보를 RCDB 에 저장하기 위한 모듈 기동 처리
if( objCacheInsert.Start() == false )
{
LOG( LERR, "Worker[%d]: Complete cache insert module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CCacheInsert module start.", getpid() );
// Cache 요청을 확인하여 Job 에 등록을 수행하는 Thread 모듈 기동 처리
if( objCacheOrder.Start() == false )
{
LOG( LERR, "Worker[%d]: Cache job order module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CCacheOrder module start.", getpid() );
// Cache 생성 Thread 제어 모듈 기동 처리
if( objCacheControl.Start() == false )
{
LOG( LERR, "Worker[%d]: Cache control module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CCacheControl module start.", getpid() );
// 모듈 초기화 및 기동 처리 완료.
/////////////////////////////////////////////////////////////////////
// worker 프로세스의 작업 수행.
// fimngd 로 부터 hot content 정보를 수신 처리한다.
int clientSocket;
struct sockaddr_in clientSockAddr;
socklen_t clientSockLen = sizeof( clientSockAddr );
pthread_t clientThreadId;
struct stFhsReceiveThreadArg * pThreadArg = NULL;
while( 1 )
{
clientSocket = accept( socketListenHotContent, ( struct sockaddr * ) &clientSockAddr, &clientSockLen );
if( clientSocket == -1 )
{
// 오류발생시 해당 내역 로깅처리.
int errorNum = errno;
LOG( LERR, "Worker: fimngd client accept failed. [%d][%s]", errorNum, strerror( errorNum ) );
}
else
{
// 정상적인 Client 인 경우...
// Thread 로 전달할 변수를 malloc 로 생성 처리
pThreadArg = ( struct stFhsReceiveThreadArg * ) malloc( sizeof( struct stFhsReceiveThreadArg ) );
if( pThreadArg == NULL )
{
int errorNum = errno;
close( clientSocket );
char tempBuffer[INET_ADDRSTRLEN];
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), tempBuffer, sizeof( tempBuffer ) ) != NULL )
{
LOG( LERR, "[%s] hot content receive thread malloc failed [%d][%s]", tempBuffer, errorNum, strerror( errorNum ) );
}
else
{
LOG( LERR, "hot content receive thread malloc failed [%d][%s] and inet_ntop error[%d][%s]"
, errorNum, strerror( errorNum ), errno, strerror( errno ) );
}
continue;
}
else
{
// 인자로 전달할 변수정보 설정.
pThreadArg->clientSocketDesc = clientSocket;
pThreadArg->pQueueCacheRequest = &queueCacheRequest;
char tempBuffer[INET_ADDRSTRLEN];
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), tempBuffer, sizeof( tempBuffer ) ) != NULL )
{
memcpy( pThreadArg->szClientAddress, tempBuffer, INET_ADDRSTRLEN );
}
else
{
snprintf( pThreadArg->szClientAddress, INET_ADDRSTRLEN - 1, "0.0.0.0" );
}
// fimngd 로부터 hot content 정보를 수신할 Thread 생성 처리
if( pthread_create( &clientThreadId, NULL, threadHotContentReceive, (void *)pThreadArg ) != 0 )
{
// 생성 실패시...
// - 로깅
// - client socket close
// - malloc 로 생성된 전달 변수 free 처리.
int errorNum = errno;
LOG( LERR, "[%s] hot content receive thread create failed [%d][%s]"
, pThreadArg->szClientAddress, errorNum, strerror( errorNum ) );
close( clientSocket );
free( pThreadArg );
}
}
}
} // while( 1 )
// Process 종료 처리.
_LOG( LINF, "Worker[%d] exit.. Good Bye..", getpid());
//잠시 대기 후 종료처리.
// CHG 2015-09-14 huibong
// - #24369 usleep -> nanosleep 으로 변경 처리.
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
// fork 를 수행한 함수에서 exit 함수 호출을 통한 종료처리
return EXIT_SUCCESS;
}
// FHS 의 fimngd 로 부터 TCP 연결 요청을 받아...
// Hot Content 정보를 수신 처리하기 위한 Thread
void * threadHotContentReceive( void * arg )
{
// 전달받은 변수를 내부 변수에 저장 처리.
// client socket 관련 처리 변수 생성
CFimngdSocketControl clientSocket( ( ( struct stFhsReceiveThreadArg * ) arg )->clientSocketDesc );
// client 주소 처리 관련
char szClientAddress[INET_ADDRSTRLEN];
memcpy( szClientAddress, ( ( struct stFhsReceiveThreadArg * ) arg )->szClientAddress, INET_ADDRSTRLEN );
// 수신된 정보를 저장하기 위한 queue 에 대한 포인터
CDataQueue<CCacheRequestData> * pQueueCacheRequest = ( ( struct stFhsReceiveThreadArg * ) arg )->pQueueCacheRequest;
// malloc 로 생성된 변수 해제 처리
free( arg );
// 본 thead 는 client 연결 상태에 따라 종료되므로.. process 에서 분리 처리
pthread_detach( pthread_self() );
_LOG( LDBG, "hot content recieve thread start [%s]", szClientAddress );
// FHS fimngd 로 부터 data 수신 대기
int nRead;
CCacheRequestData receiveData;
while( 1 )
{
// Data 수신 객체 초기화.
receiveData.Init();
// Data 수신
nRead = clientSocket.GetData( receiveData );
if( nRead == 0 )
{
// timeout 발생시
// - fimngd 는 data 전송 후.. 추가 전송할 data 가 있는지 일정기간 세션을 유지함.
// - 따라서 .. 잠깐 sleep 후.. 다시 수신 시도.한다.
sleep( 3 );
continue;
}
else if( nRead < 0 )
{
// Socket 오류 발생시
// - 관련 로그는 해당 모듈에서 다 찍었으므로.. 여기서는 루프 종료 처리.
break;
}
else
{
// 정상 Data 수신시 유효 요청인지 검사한다.
if( CLogger::GetInstance() != NULL && CLogger::GetInstance()->GetLogLevel() >= LDBG )
{
receiveData.Print();
}
// 1. RCID 확인
if( strcasecmp( receiveData.m_strRcid.c_str(), CServiceConfig::GetInstance()->GetRcId() ) != 0 )
{
// RCID 가 유효하지 않은 경우..
// - 잘못된 세션 요청이므로...
// - 로깅, Report 로깅 후 세션 종료 처리한다.
_LOG( LWAR, "cache request: rc id mismatch.[%s][%s]"
, receiveData.m_strRcid.c_str()
, receiveData.m_strRequestHost.c_str() );
CReportLog reportLog;
if( reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
{
std::string strErrorMessage;
reportLog.GetLastError( strErrorMessage );
LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
}
else
{
reportLog.Write( "ERR", "[%s][Cache request RC ID mismatch. check FHS[%s][%s]]"
, PROG_NAME
, receiveData.m_strRequestHost.c_str()
, receiveData.m_strRcid.c_str() );
}
// 세션 종료 처리.
break;
}
// 2. filename_hash 정보가 정확한지
if( receiveData.m_strFileNameHash.empty() == true )
{
// filename_hash 정보가 존재하지 않는 경우..
// - 현재 수신된 정보는 버리고.. 다시 수신 대기
_LOG( LWAR, "cache request: file name empty. [%s]"
, receiveData.m_strRequestHost.c_str() );
// 다시 수신 대기
continue;
}
// 3. szServiceID ( 구 Tran ID 정보) 가 정확한지 검사
if( receiveData.m_strServiceSeq.empty() == true )
{
// 서비스 ID 정보가 존재하지 않는 경우..
// - 현재 수신된 정보는 버리고.. 다시 수신 대기
_LOG( LWAR, "cache request: service sequence empty. [%s]"
, receiveData.m_strRequestHost.c_str() );
// 다시 수신 대기
continue;
}
// 4. Content Length 값이 0 이상인지....
if( receiveData.m_uContentLength == 0 )
{
_LOG( LWAR, "cache request: content length 0. [%s][%s]"
, receiveData.m_strRequestHost.c_str()
, receiveData.m_strFileNameHash.c_str() );
// 다시 수신 대기
continue;
}
// 5. 동접 FD 의 값이 conf 에 설정된 값 이상인지 검사
if( receiveData.m_uConcurrentFd < CServiceConfig::GetInstance()->GetHotContentCreateMinimumFd() )
{
// Cache 생성 요청의 동접 FD 값이 conf 의 설정값 미만인 경우....
// - 다음 data 수신 대기
_LOG( LDBG, "cache request: fd count lower. ignored. [%u/%u] [%s][%s]"
, receiveData.m_uConcurrentFd
, CServiceConfig::GetInstance()->GetHotContentCreateMinimumFd()
, receiveData.m_strRequestHost.c_str()
, receiveData.m_strFileNameHash.c_str() );
continue;
}
// 유효 요청인 경우...
// - Queue 에 Push 처리한다.
// - 만약 queue 가 full 이라 push 가 실패나는 경우.. 3번 재시도, 안되면 버린다.
int nRetry = 0;
while( pQueueCacheRequest->Push( receiveData ) == false )
{
++nRetry;
if( nRetry > 3 )
{
_LOG( LWAR, "Cache request push to queue fail. ignored [%s][%s]"
, receiveData.m_strRequestHost.c_str()
, receiveData.m_strFileNameHash.c_str() );
break;
}
else
{
sleep( 1 );
}
}
}
}
// client 와 연결 종료 처리.
_LOG( LDBG, "hot content recieve connection closed.[%s]", szClientAddress );
clientSocket.Close();
return NULL;
}
/// @brief Worker Process 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
/// @param nSignalNumber [in] 발생한 시그널 Number
/// @return void
static void SignalWorkerTerminate(int nSignalNumber)
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "Worker[%d] process exit by user signal [SIGTERM]. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "Worker[%d] process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
// CHG 2015-09-14 huibong
// - #24369 usleep -> nanosleep 으로 변경 처리.
struct timespec sleep;
sleep.tv_sec = 0;
sleep.tv_nsec = 500000000; // 0.5 sec
nanosleep( &sleep, NULL );
exit( EXIT_SUCCESS );
}
/// @brief Worker 프로세스 signal 처리 설정을 위한 함수
/// @return void
void SetSignalWorker( void )
{
sigset_t set;
struct sigaction act;
memset(&act, 0x00, sizeof(act));
sigfillset( &set );
sigprocmask( SIG_SETMASK, &set, NULL );
sigfillset( &act.sa_mask );
/* 무시할 신호 목록 */
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 = SignalWorkerTerminate;
sigaction( SIGTERM, &act, NULL); /* kill -TERM 에 의한 프로세스 종료시 */
sigemptyset(&set); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask(SIG_SETMASK, &set, NULL);
}
+50
View File
@@ -0,0 +1,50 @@
/***************************************************************************
rc_rmcd Worker Header ( Worker.h )
-----------------------------------------
begin : 2010/03/09
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 1.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 __RC_RMCD_WORKER_H__
#define __RC_RMCD_WORKER_H__
#ifdef __cplusplus
extern "C" {
#endif
// Worker Process 의 main 함수
// @param socketListenHotContent fimngd 로 부터 Hot Content 정보 수신을 위한 TCP Listen socket
// @return EXIT_FAILURE 모듈 초기화 실패시
// EXIT_SUCCESS 그 외 정상 종료
int WorkerMain( int socketListenHotContent );
/// @brief Worker 프로세스 signal 처리 설정을 위한 함수
/// @return void
void SetSignalWorker( void );
// FHS 의 fimngd 로 부터 TCP 연결 요청을 받아...
// Hot Content 정보를 수신 처리하기 위한 Thread
void * threadHotContentReceive( void * arg );
/* Signal 처리를 위한 각 Signal Handler 함수는
* 다른 Code 에서 Include 처리시 Static 관련 문제로 인해
* 본 Header 에서 선언처리 하지 않음. cpp 에만 존재
*/
#ifdef __cplusplus
}
#endif
#endif /* __RC_RMCD_WORKER_H__ */