본문 바로가기
프로그래밍/Redis

Windows MFC에서 Redis Client 이용하기

by 남생 namsaeng 2022. 3. 14.
반응형

데이터 수집, 가공, 처리와 관련된 응용프로그램을 개발할 때 C++ 언어를 사용하는 윈도 응용프로그램 개발 프레임워크인 MFC(Microsoft Foundatin Class Library)를 이용할 경우가 있다. 여러 이유가 있겠지만, 방산업계에서는 C++ 언어를 이용할 때 신뢰성 검사가 무료(C#은 유료)라 사용하고, UI 개발자들은 이벤트 컨트롤을 섬세하게 다룰 수 있기 때문에 MFC를 이용한 C++ 응용프로그램을 개발한다. 여기에 공유 데이터 사용의 필요성 및 데이터 캐싱을 위하여 DBMS(Database Management System)를 이용하는데, 빠른 데이터 캐싱이 주요 목적이라면, Disk-based DBMS보다 In-memory DBMS의 사용이 적절하다. 그중, Redis는 무료이면서 세계에서 가장 빠른 In-memory cache DBMS이기 때문에 많은 개발자들로부터 사랑을 받고 있다. 데이터 공유를 위해 DB서버를 구축하여 Redis 인스턴스를 띄우거나 가볍게 사용할 용도로 클라이언트 호스트에 바로 Redis 인스턴스를 띄우는 경우가 있는데, 이때 클라이언트 프로그램으로 Redis 인스턴스에 접근해야 한다. Redis 클라이언트 라이브러리 중 MFC에서 사용할 수 있는 대표적인 라이브러리 3개를 소개한다.

1. hiredis

  • - C언어로 작성된 Redis 데이터베이스 클라이언트 라이브러리로, 최소한의 redis 지원만 제공하는 특징이 있다. hiredis는 최근 로그 데이터 처리를 위해 Redis 5.0부터 도입된 스트림(Stream) 데이터 타입을 잘 다루지 못한다.


[사용방법]
1) CMake 사이트(https://cmake.org/download/)에서 “.msi”파일을 다운로드하여 설치한다.

 

Download | CMake

Current development distribution Each night binaries are created as part of the testing process. Other than passing all of the tests in CMake, this version of CMake should not be expected to work in a production environment. It is being produced so that us

cmake.org

 

2) hiredis 사이트(https://github.com/redis/hiredis)에서 “.zip”파일을 다운로드하고 압축을 푼다. 기본 이름이 “hiredis-master”로 되어있을 것이다.

 

GitHub - redis/hiredis: Minimalistic C client for Redis >= 1.2

Minimalistic C client for Redis >= 1.2. Contribute to redis/hiredis development by creating an account on GitHub.

github.com

 


3) 1)에서 설치한 CMake GUI를 관리자 권한으로 실행한 후에 [Where is the source code]에 “hiredis-master”에서 “CMakeList.txt” 파일이 있는 디렉터리 경로까지 설정하고, [Where to build the binaries]에는 “(Where is the source code)/build”까지 설정한다.


4) 하단의 “Configure” 버튼을 클릭하고, 현재 자신이 사용하고 있는 Visual Studio 컴파일러 버전을 설정한 후, “Finish” 버튼을 클릭한다.



5) [CMAKE_INSTALL_PREFIX]에 빌드 후에 생기는 라이브러리 파일을 저장하고 싶은 경로를 설정한 후에 “Generate” 버튼을 클릭한다.

6) Visual Studio를 관리자 권한으로 실행한 후 [로컬 폴더 열기]에서 “(Where is the source code)/build” 디렉토리를 오픈하면, “hiredis.sln” 파일이 있는데 이것을 클릭한다. (혹은 CMake GUI에서 “Open Project”를 클릭한다.)

  • “ALL_BUILD”를 빌드한 후에 “INSTALL”을 빌드한다.


7) “Debug” 버전과 “Release” 버전에서 각각 6)의 과정을 수행한다.

8) 프로젝트 속성 페이지에서 다음을 추가한다.

  • [구성 속성] > [디버깅] > [환경] : PATH=$(ProjectDir)\bin;%PATH%;
  • [구성 속성] > [C/C++] > [추가 포함 디렉터리] : $(ProjectDir)\include;
  • [구성 속성] > [링커] > [일반] > [추가 라이브러리 디렉터리] : $(ProjectDir)\lib;
  • [구성 속성] > [링커] > [입력] > [추가 종속성] : hiredis.lib;


9) 5)에서 [CMAKE_INSTALL_PREFIX]에 입력한 경로로 가서 “bin”, “include”, “lib” 디렉터리를 우리의 프로젝트 디렉터리에 복사한다.

  • 예시) C:\Users\SIU\source\repos\redisTest\redisTest 에 3개의 디렉터리 복사



10) 사용할 소스코드 상단에 #include <hiredis/hiredis.h>를 추가하고 redis-server에 접근하는 구문을 작성한다.

	redisContext* conn = NULL;
	redisReply* resp = NULL;
	int loop = 0;

	conn = redisConnect("192.168.1.2", 16379);

	if ((NULL != conn) && conn->err) {
		printf("error : %s\n", conn->errstr);
    	redisFree(conn);
    	exit(-1);
	}
	else if (NULL == conn) {
		exit(-1);
	}

while (++loop) {
	signal(SIGINT, signal_callback_handler);
    //resp = (redisReply*)redisCommand(conn, "xadd pdw2 * hi roo2");
    //resp = (redisReply*)redisCommand(conn, "xread block 1000 streams pdw1 $");
    //resp = (redisReply*)redisCommand(conn, "xrange pdw2 1646199467950 1646199468000");
    
    if (NULL == resp) {
    	printf("1\n");
        redisFree(conn);
        exit(-1);
    }
    if (REDIS_REPLY_ERROR == resp->type) {
    	printf("2\n");
        redisFree(conn);
        freeReplyObject(resp);
        exit(-1);
    }
    printf("%d : %s\n", loop, resp->str);
    freeReplyObject(resp);
    
    if(loop == 1000)
    	break;
}
redisFree(conn);

 

  • “xadd pdw2 *hi roo2”는 redis-server에 문자열 그대로 넘기는 것이므로 잘 실행된다.
  • “xread block 1000 streams pdw1 $”는 redis-server에 있는 1초 내에 들어오는 실시간 스트림 데이터를 받아오는 구문인데 결과 값이 (null)이다.
  • “xrange pdw2 1646199467950 1646199468000”는 redis-server에 있는 특정 시간대의 스트림 데이터를 받아오는 구문인데 결과 값이 (null)이다.
  • 결론: hiredis는 최소한의 지원만 제공하기 때문에 최근에 나온 스트림 데이터 타입을 지원하지 않는 것 같다.



2. redis-plus-plus(http://github.com/sewenew/redis-plus-plus#windows-support)

 

GitHub - sewenew/redis-plus-plus: Redis client written in C++

Redis client written in C++. Contribute to sewenew/redis-plus-plus development by creating an account on GitHub.

github.com

  • C++ 언어로 작성된 Redis 데이터베이스 클라이언트 라이브러리이다. 특징으로는 hiredis를 기반으로 돌아가기 때문에 hiredis와 같이 설치해주어야 한다는 점이다. redis-plus-plus는 컴파일러 버전 visual studio 2022(C++ 17), visual studio 2015(C++ 14), 및 visual studio 2012(C++ 11)와 호환된다고 한다.
  • CMake GUI 과정은 hiredis 과정과 비슷하다.

 

  • [CMAKE_CINSTALL_PREFIX] : 빌드 후에 생기는 라이브러리 파일을 저장하고 싶은 경로
  • [HIREDIS_HEADER] : 위의 hiredis에서 나온 include 디렉터리
  • [HIREDIS_LIB] : 위의 hiredis에서 나온 hiredis.lib 파일
  • [TEST_HIREDIS_LIB] : 위의 hiredis에서 나온 hiredis_static.lib 파일
  • [hiredis_DIR] : 다운로드하였던 hiredis-master 파일

 

  • “Generate” 버튼을 클릭하면 [hiredis_DIR] 부분은 ‘hiredis_DIR-NOTFOUDN’라고 나온다. 이유는 잘 모르겠다.
  • [CMAKE_INSTALL_PREFIX]에 입력한 경로로 가서 “bin”, “include”, “lib” 디렉터리 내의 파일들을 우리의 프로젝트에 있는 “bin”, “include”, “lib”에 복사한다.
  • visual studio 2012로 예제 프로젝트를 생성하고, 사용할 소스코드 상단에 #include <sw/redis++/redis++.h>를 추가하고 redis-server에 접근하는 구문을 작성한다. 하지만 다음과 같은 에러가 나타났다.

 

1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(9): warning C4083: '상수'이(가) 있어야 하는데 '_STL_WARNING_LEVEL' 식별자가 있습니다.
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(10): warning C4083: ')'이(가) 있어야 하는데 '_STL_DISABLED_WARNINGS' 식별자가 있습니다.
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(15): error C2143: 구문 오류 : ';'이(가) 'namespace' 앞에 없습니다.
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(15): error C4430: 형식 지정자가 없습니다. int로 가정합니다. 참고: C++에서는 기본 int를 지원하지 않습니다.
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(29): error C2146: 구문 오류 : ';'이(가) 'initializer_list' 식별자 앞에 없습니다.
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(58) : 컴파일 중인 클래스 템플릿 인스턴스화 'std::initializer_list<_Elem>'에 대한 참조를 확인하십시오.
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(29): error C4430: 형식 지정자가 없습니다. int로 가정합니다. 참고: C++에서는 기본 int를 지원하지 않습니다.
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(29): error C3646: 'noexcept' : 알 수 없는 재정의 지정자입니다.
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\initializer_list(34): error C2146: 구문 오류 : ';'이(가) 'initializer_list' 식별자 앞에 없습니다.

- 이번에는 CMake GUI의 [REDIS_PLUS_PLUS_CXX_STANDARD]를 17로하고 visual studio 2019(ISO C++17 표준)로 예제 프로젝트를 생성했다. 사용할 소스코드 상단에 #include <sw/redis++/redis++.h>를 추가하고 redis-server에 접근하는 구문을 작성한다. 하지만 다음과 같은 에러가 나타났다.

오류 개수 1060개
C4430: 형식 지정자가 없습니다. int로 가정합니다. 참고: C++에서는 기본 int를 지원하지 않습니다.

 

  • 왜 에러가 나는지 모르겠다.



3. cpp_redis(http://github.com/Cylix/cpp_redis)



1) 위의 주소에서 “tacopie @ 8714fce” 링크를 클릭해 해당 “. zip”파일을 다운로드한다.

2) 다운로드한 디렉터리의 압축을 풀고 ..\tacopie\msvc15로 가보면 tacopie.sln 있는데 실행시켜서 빌드한다.

3) 빌드를 완료하면 ..\tacopie\msvc15\x64\Debug 또는 Release 디렉터리에 tacopie.lib이 있을 것이다.

4) 다시 위의 cpp_redis 설치 사이트로 들어가서 “. zip”파일을 다운로드한다.

5) 다운로드한 디렉터리의 압축을 풀고 CMake GUI를 실행해 아래와 같이 입력한다. “tacopie @ 8714fce” 링크를 클릭해 다운받은 디렉터리명은 “tacopie”로 가정했다.


6) redis를 사용할 우리의 프로젝트 디렉터리 하위 경로에 다음 경로를 추가한다.
- ..\includes\
- ..\includes\tacopie

7) CMake GUI에서 “Open Project”를 클릭해서 빌드하면, [CMAKE_INSTALL_PREFIX] 경로에 include\cpp_redis 폴더가 있을 것이다. 이것을 상기 ..\includes\에 복사한다.

8) 상기 ..\includes\tacopie 디렉터리 안에, 다운로드하였던 tacopie의 includes\tacopie\ 아래에 있는 network 디렉터리, utils 디렉터리, tacopie 파일을 복사한다.

9) 빌드 완료 후 생긴 tacopie.lib도 복사해서 넣어준다.

9-1) [CMAKE_INSTALL_PREFIX] 경로에서 ..₩lib₩Debug 안에 있는 cpp_redis.lib도 상기 ..₩includes₩cpp_redis로 복사해서 넣어준다.


10) hiredis의 8) 번 과정처럼 프로젝트 속성이 설정되어 있는지 확인한다.

11) 사용할 소스코드 상단에 #include <cpp_redis/cpp_redis>를 추가하고 redis-server에 접근하는 구문을 작성한다.

cpp_redis::client client;
client.connect("192.168.1.2", 16379);
client.sync_commit();

char pdw1_buff[100];
PDW pdw1;

while(1) {
	client.send({"xread", "block", "1000", "streams", "FFT ", "PDW1", "PDW2", "$", "$", "$"}, [ &pdw1_buff, &resp, &pdw1](cpp_redis::reply& reply){
    	if(reply.is_array()){
        	for(auto key : reply.as_array()){
            	if(key.is_array()){
                	for(auto key2 : key.as_array()){
                    	if(key2.is_array()){
                        	for(auto key3 : key2.as_array()){
								if(key3.is_array()){
                                	for(auto key4 : key3.as_array()){
                                    	if(key4.is_array()){
                                        	int cnt = 0;
                                            for(auto key5 : key4.as_array()){
                                            	if(key5.is_string()){
                                                	if(cnt == 1){
                                                    	strcpy_s(pdw1_buff, key5.as_string().c_str());
                                                    }
                                                    cnt++;
                                                }
                                             }
                                         }
                                         else {
                                         	cout << key4.as_string() << " ";
                                         }
                                     }
                                 }
                                 else
                                 {
                                 }
                             }
                         }
                         else
                         {
                         	cout << key2.as_string() << " ";
                         }
                     }
                 }
             }
         }
     });
client.sync_commit();

//strcpy(pdw1_buff, resp.c_str());
std::memcpy((char *)&pdw1, pdw1_buff, sizeof(PDW));
std::cout << pdw1.frequency << endl;
resp.clear();


12) redis db는 필드로 BLOB(Binary Large Object)를 최대 512MB의 용량까지 넣을 수 있는데, 무료이면서 BLOB를 다루는 NoSQL In-memory DBMS는 redis가 유일할 것이다. 

13) tacopie.lib가 (/MTd)에서 돌아가기 때문에 이걸 사용하는 cpp_redis는 [MFC 사용] > [정적라이브러리에서 MFC 사용], [런타임 라이브러리] > [다중 스레드 디버그(/MTd)] 이어야 한다. 

반응형

댓글