흐르는 시간의 블로그...

현재 운영중인 시스템에서 poco library의 log시스템을 사용한다.

c++14를 사용하기 위해 최근 GCC를 5.4버전으로 올렸다.

그 과정의 수많은 삽질이 있었다. Linux를 잘아는 천과장이 아니었으면 지금도 미궁을 헤메고 있었을듯... :(


우쨋거나 GCC 5.4 업버전 이후 또 다른 문제에 봉착했다.

poco library가 정상 작동을 하지 않았다.

혹시 하는 생각에 poco-1.7.4로 업버전 하여 컴파일하여 install 하였다.

여전히 안된다.


configure를 살펴봐도 gcc 관련 내용은 없다.

여기저기 뒤져보다가 make file을 살펴보니 다름과 같은 라인이 보인다.


include $(POCO_BASE)/build/config/$(POCO_CONFIG)


시스템상 관련 파일은 ./build/config/Linux 이다

해당 부분에서 현재의 gcc5.4의 관련 부분으로 수정하여 컴파일 했더니 문제 없이 넘어가게 되었다.


#
# $Id: //poco/1.4/build/config/Linux#2 $
#
# Linux
#
# Make settings for Linux 2.6/gcc 3.3
#
#

#
# General Settings
#
LINKMODE ?= SHARED

#
# Define Tools
#
#CC      = ${CROSS_COMPILE}gcc
#CXX     = ${CROSS_COMPILE}g++
CC      = /opt/rh/gcc-5.4.0/bin/gcc
CXX     = /opt/rh/gcc-5.4.0/bin/g++


원래 나던 링크 에러 문제...


ubirloader.cpp:(.text+0x20a): 
undefined reference to `Poco::FileChannel::setProperty(std::__cxx11::basic_string<char, std::char_traits<char>, 
std::allocator<char> >  std::char_traits<char>, std::allocator<char> > const&)'
collect2: error: ld returned 1 exit status


오랜만에 수정하는 작업이 하나 있었다.

해당 서버의 mysql의 계정 설정이 하나 빠진 것이 있어 공인IP로 접속이 되지 않았다.

관련해서 이런저런 얘기를 팀원과 하다가 하나 배운 것이 있다.


바로 protocol 옵션에 대한 것이다. 실제 사용 DB는 mariadb 5.x.x 버전이다.

mysql의 메뉴얼 페이지를 보자.

4.2.2 Connecting to the MySQL Server

클릭을 하면 바로 protocol 쪽으로 이동한다.

네개의 옵션이 있다. 정확하지는 않지만 아랫쪽으로 갈수록 빠를듯 하다.

PIPE와 MEMORY는 윈도우즈에서만 사용가능하고

SOCKET은 Unix Only이다.


내용에 보듯이 unix(linux)에서 localhost로 접속하게 되면 기본적으로 Unix socket file을 사용한다는 내용이다.

당연히 TCP 보다 빠르다.

127.0.0.1로 접속하면 TCP로 접속하지만 localhost로 접속하면 unix socket으로 접속한다.


간단하지만 db의 local machine에서 작업을 해야한다면 염두에 둬야할 내용이다.


127.0.0.1로 접속 하는 경우

MariaDB [(none)]> \s
--------------
mysql  Ver 15.1 Distrib 5.5.34-MariaDB, for Linux (x86_64) using readline 5.1

Connection id:          115593510
Current database:
Current user:           finepoint@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server:                 MariaDB
Server version:         5.5.36-MariaDB-log MariaDB Server
Protocol version:       10
Connection:             127.0.0.1 via TCP/IP
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8


localhost로 접속 하는 경우

MariaDB [(none)]> \s
--------------
mysql  Ver 15.1 Distrib 5.5.34-MariaDB, for Linux (x86_64) using readline 5.1

Connection id:          115586990
Current database:
Current user:           finepoint@localhost
SSL:                    Not in use
Current pager:          stdout
Using outfile:          ''
Using delimiter:        ;
Server:                 MariaDB
Server version:         5.5.36-MariaDB-log MariaDB Server
Protocol version:       10
Connection:             Localhost via UNIX socket
Server characterset:    utf8
Db     characterset:    utf8
Client characterset:    utf8
Conn.  characterset:    utf8
UNIX socket:            /var/lib/mysql/mysql.sock

일전에 pcqueue를 간단히 만들었었다.
창피해서 코드를 올리지 말까 하다가 올려본다.
실제 사용중에 있다.
허접한 코드를 개선해서 사용해도 좋다.
요기 출처를 달아주고 개선된 부분이 있으면 공유해 주면 더 바랄게 없다.
허접하다고 너무 심하게 구박하지 말아줬으면 좋겠다.

아래는 해당 pcqueue의 템플릿 코드이다.
실제 호출은 템픗릿 소스의 아래에 올려 놓겠다.

//---------------------------------------------------------------------------------------------------------------------
// pcqueue.hpp
//
// Asker's product consumer queue
//---------------------------------------------------------------------------------------------------------------------
#pragma once

#include <iostream>
#include <vector>
#include <pthread.h>

#include "errno.h"

#include "common.hpp"

using namespace std;
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------
template <class T>
class CPCQueue
{
private:
	pthread_mutex_t ProducerLock;
	pthread_mutex_t ConsumerLock;

	vector<T*> ProducerQueue;
	vector<T*> ConsumerQueue;

private:
	bool LockProducer()
	{
		int _nError = pthread_mutex_lock( &ProducerLock );
		if ( _nError )
		{
			cout << "error in lock producer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in lock producer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}
	
		return true;
	}

	bool UnlockProducer()
	{
		int _nError = pthread_mutex_unlock( &ProducerLock );
		if ( _nError )
		{
			cout << "error in unlock producer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in unlock producer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}
	
		return true;
	}

	bool LockConsumer()
	{
		int _nError = pthread_mutex_lock( &ConsumerLock );
		if ( _nError )
		{
			cout << "error in lock consumer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in lock consumer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}

		return true;
	}

	bool UnlockConsumer()
	{
		int _nError = pthread_mutex_unlock( &ConsumerLock );
		if ( _nError )
		{
			cout << "error in unlock consumer. return : " << _nError << ", err :" << strerror(errno) << endl;
			formatlog( LOG_CRIT, "error in unlock consumer. return(%d) ErrNo(%d) ErrMsg(%s)", _nError, errno, strerror(errno));
			return false;
		}

		return true;
	}


public:
	CPCQueue(){}
	virtual ~CPCQueue()
	{
		for ( class vector<T*>::iterator _iter = ProducerQueue.begin(); _iter != ProducerQueue.end(); _iter++)
			delete *_iter;

		for ( class vector<T*>::iterator _iter = ConsumerQueue.begin(); _iter != ConsumerQueue.end(); _iter++)
			delete *_iter;

		ProducerQueue.clear();
		ConsumerQueue.clear();
	}

	bool Init()
	{
		int _ErrorNo = pthread_mutex_init( &ProducerLock, NULL);
		if (_ErrorNo)
			return false;
	
		_ErrorNo = pthread_mutex_init( &ConsumerLock, NULL);
		if (_ErrorNo)
			return false;
	
		return true;
	}

	// Product에 하나를 추가한다
	bool AddProduct( T* _Job )
	{
		if ( false == LockProducer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		ProducerQueue.push_back(_Job);
	 
		if ( false == UnlockProducer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		return true;
	}

	// Consumer 쪽에서 하나씩 빼낸다.
	bool PopFromConsumer(T** _Value)
	{
		*_Value = NULL;

		if ( false == LockConsumer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		if (!ConsumerQueue.empty())
		{
			*_Value = ConsumerQueue.front();
			ConsumerQueue.erase(ConsumerQueue.begin());
		}

		if ( false == UnlockConsumer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		return true;
	}

	// 합할 것이 하나도 없는 경우 false를 반환한다
	bool MoveProducerToConsumer(int& _MoveCount)
	{
		if ( false == LockConsumer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		if ( false == LockProducer())
		{
			// lock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Lock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		_MoveCount = ProducerQueue.size();
		if (!ProducerQueue.empty())
		{
			for ( class vector<T*>::const_iterator _iter = ProducerQueue.begin(); _iter != ProducerQueue.end(); _iter++)
				ConsumerQueue.push_back(*_iter);
	
			ProducerQueue.clear();
		}

		if ( false == UnlockProducer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		if ( false == UnlockConsumer())
		{
			// unlock 실패
			formatlog( LOG_CRIT, "CPCQueue::%s(%d) Unlock Fail. fatal issue.", __func__, __LINE__);
			return false;
		}

		return true;
	}

	int GetConsumerSize() const
	{
		return ConsumerQueue.size();
	}

};
//---------------------------------------------------------------------------------------------------------------------



class CDataThread: public CUbiThread
{
private:
	CPCQueue<COnePacket> PCQueue;
	int ThreadIndex;

	CURL* ctx;

public:
	bool Terminated;

public:
	CDataThread();
	virtual ~CDataThread();

	bool init();
	void SetIndex(int _Index);
	bool AddProduct( COnePacket* _OnePacket);

	virtual void Run();

private:
	bool DoPacketProcess(COnePacket* _OnePacket);
	CUbiParser* MakeParser(COnePacket* _OnePacket);
}

//---------------------------------------------------------------------------------------------------------------------
CDataThread::CDataThread()
{
}
//---------------------------------------------------------------------------------------------------------------------
CDataThread::~CDataThread()
{
}
//---------------------------------------------------------------------------------------------------------------------
bool CDataThread::init()
{
	return PCQueue.Init();
}
//---------------------------------------------------------------------------------------------------------------------
void CDataThread::SetIndex(int _Index)
{
	ThreadIndex = _Index;
}
//---------------------------------------------------------------------------------------------------------------------
bool CDataThread::AddProduct( COnePacket* _OnePacket )
{
	return PCQueue.AddProduct(_OnePacket);
}
//---------------------------------------------------------------------------------------------------------------------
void CDataThread::Run()
{
	int _Count = 0;
	int _JobCount = 0;
	int _SleepMicro;
	COnePacket* _OnePacket = NULL;

	Terminated = false;
	while(!Terminated)
	{
		try
		{
			// Customer Queue에 Producer Queue의 내용을 가져오게 한다
			if (!PCQueue.MoveProducerToConsumer(_Count))
				formatlog( LOG_FATAL, "CDataThread::%s(%d) %10d Thread Error in Move PCQueue data", __func__, __LINE__, ThreadIndex); //, typeid(T).name());
			else
				formatlog( LOG_INFO, "CDataThread::%s(%d) %10d Thread %2d %3d remain.", __func__, __LINE__, ThreadIndex, _Count, PCQueue.GetConsumerSize());
		}
		catch(exception &_innerException)
		{
			formatlog( LOG_CRIT, "Exception on CDataThread::%s(%d). %2d Thread what(%s)", __func__, __LINE__, ThreadIndex, _innerException.what());
		}

		// Customer Queue를 비울때 까지 작업을 처리 한다
		_JobCount = 0;
		while(true)
		{
			_OnePacket = NULL;

			// 처리 간격에 일정 시간을 둔다
			if (!PCQueue.PopFromConsumer(&_OnePacket))
			{
				formatlog( LOG_FATAL, "CDataThread::%s(%d). %2d Thread Error in Get Data From PCQueue", __func__, __LINE__, ThreadIndex);
				break;
			}

			if ( NULL == _OnePacket)
				break;

			//1 실질적인 패킷 처리!!
			DoPacketProcess(_OnePacket);
			_JobCount++;

			delete _OnePacket;
		}

		usleep(500000 );
	}
}


내 업무는 아니지만 회사 업무에서 Java / iBatis를 사용하는 케이스가 있다.


처음에는 설정 파일을 보고 Pool이란 단어를 지나쳐서 Pool을 사용하지 않는지 알았다.

특히 DB에 일련의 특별한 connection pool을 사용하고 있는 것 처럼 보이지 않아서 였다.


<transactionManager type="JDBC" > 

<dataSource type="SIMPLE">

                   ..............................

<property name="Pool.MaximumActiveConnections" value="10"/>

<property name="Pool.MaximumIdleConnections" value="5"/>

<property name="Pool.MaximumCheckoutTime" value="20000"/>

<property name="Pool.TimeToWait" value="10000"/>

</dataSource>

</transactionManager>


다음의 링크에 가면 자세한 설명이 있다.

결국 iBatis에서 관리하는 connection pool을 사용하다는 얘기다.


IBATIS MaximumIdleConnections / Connection pooling logic (SimpleDataSource)

'프로그래밍???' 카테고리의 다른 글

Too Slow JDBC Connection  (0) 2016.10.10
GO Lang 시작  (0) 2016.09.22
Redis 시작...  (0) 2015.12.11
DNS의 타입에 대해...  (0) 2015.04.21
엑셀에서 랜덤 문자열 생성하는 VBA 스크립트  (0) 2015.03.04