풀타임으로 일하고 있습니다. 멤베이스를 사용하는 "엔진 인터페이스"에 추가하고 있습니다. 멤캐시드. API를 설계하고 문서를 작성한 사람으로서, 저는 누구를 모욕하지 않으면서도 더 많은(그리고 더 나은) 문서가 필요하다고 말할 수 있습니다. 이 블로그 글은 나만의 스토리지 엔진을 작성하는 방법에 대한 미니 튜토리얼의 첫 번째 글입니다. 서버의 파일에 모든 키를 저장하는 엔진을 구축하는 동안 엔진 인터페이스의 모든 측면을 다루려고 노력할 것입니다.
이 항목에서는 개발 환경을 설정하는 기본 단계와 엔진의 수명 주기에 대해 다룹니다.
개발 환경 설정
가장 쉽게 '실행'하는 방법은 엔진 인터페이스의 개발 브랜치를 설치하는 것입니다. 다음 명령을 실행하기만 하면 됩니다:
$ CD 멤캐시드
$ git -b 엔진 오리진/엔진
$ ./config/autorun.sh
$ ./configure -prefix=/opt/memcached
$ 모두 설치
다음 명령을 실행하여 서버가 작동하는지 확인합니다:
$ 에코 버전 | nc localhost 11211
버전 1.3.3_433_g82fb476 ≶- 다른 출력 문자열이 나타날 수 있습니다....
$ fg
$ ctrl-C
파일 시스템 엔진 만들기
자동 설정을 사용하여 엔진을 빌드하고 싶을 수도 있지만 자동 설정을 설정하는 것은 이 튜토리얼의 범위를 벗어납니다. 그냥 다음을 사용하겠습니다. 메이크파일
대신
INCLUDE=-I${ROOT}/include
#CC = gcc
#CFLAGS=-std=gnu99 -g -DNDEBUG -fno-strict-aliasing -Wall
# -지정된 프로토타입 -지정되지 않은 프로토타입 -지정되지 않은 선언
# -Wredundant-decls
# ${INCLUDE} -dhave_config_h
#LDFLAGS=공유
CC=cc
CFLAGS=-I${ROOT}/include -m64 -xldscope=hidden -mt -g
-errfmt=error -errwarn -errshort=tags -KPIC
LDFLAGS=-G -z defs -m64 -mt
모두: .libs/fs_engine.so
설치: 모두
${CP} .libs/fs_engine.so ${ROOT}/lib
SRC = fs_engine.c
OBJS = ${SRC:%.c=.libs/%.o}
.libs/fs_engine.so: .libs $(OBJS)
${LINK.c} -o $@ ${OBJS}
.libs:; -@mkdir $@
.libs/%.o: %.c
${COMPILE.c} $< -o $@ clean: $(RM) .libs/fs_engine.so $(OBJS)
저는 Solaris에서 대부분의 개발을 Sun Studio 컴파일러를 사용하여 하고 있지만, gcc를 사용하시는 분들을 위해 gcc에 대한 설정 섹션을 추가했습니다. 다음에 대한 줄을 주석 처리하기만 하면 됩니다. CC
, CFLAGS
그리고 LDFLAGS
를 클릭하고 #
를 클릭하세요.
멤캐시가 스토리지 엔진을 활용하려면 먼저 모듈을 로드한 다음 엔진 인스턴스를 생성해야 합니다. 이때 -E
옵션을 추가하여 memcached가 로드할 모듈의 이름을 지정할 수 있습니다. 모듈이 로드되면 memcached는 다음과 같은 이름의 심볼을 찾습니다. create_instance
를 사용하여 멤캐시가 엔진과 통신하는 데 사용할 수 있는 핸들을 생성합니다. 이것이 우리가 만들어야 할 첫 번째 함수이며, 다음과 같은 서명을 가져야 합니다:
ENGINE_ERROR_CODE create_instance(uint64_t 인터페이스, GET_SERVER_API get_server_api, ENGINE_HANDLE **handle);
이 함수의 목적은 서버에 모듈에 대한 핸들을 제공하는 것이지만, 다음과 같이 해야 합니다. not 아직 엔진 초기화를 수행하지 않았습니다. 그 이유는 멤캐시드 서버가 당사가 제공하는 API 버전을 지원하지 않을 수 있기 때문입니다. 서버가 지원하는 "가장 높은" 인터페이스 버전을 엔진에 알려야 한다는 의도입니다. 인터페이스
및 엔진 필수 를 통해 해당 인터페이스 중 하나에 대한 설명자를 반환합니다. handle
. 엔진이 하지 마십시오. 반환해야 하는 인터페이스 중 하나를 지원해야 합니다. ENGINE_ENOTSUP
.
이제 예제 엔진에 대한 엔진 설명자를 정의하고 다음에 대한 구현을 만들어 보겠습니다. create_instance
:
ENGINE_HANDLE_V1 엔진;
/* 이 구조는 나중에 확장할 예정입니다 */.
};
멤캐시드_공개_API
ENGINE_ERROR_CODE create_instance(uint64_t 인터페이스,
GET_SERVER_API GET_SERVER_API,
ENGINE_HANDLE **핸들) {
/*
* 서버의 인터페이스가 당사가 지원하는 인터페이스인지 확인합니다. 지금 바로
* 인터페이스가 하나뿐이므로 모든 인터페이스를 허용합니다.
* 거부 여부는 서버에 달려 있습니다... 여기에 테스트를 추가합니다.
* 사진 보기...
*/
if (인터페이스 == 0) {
엔진_엔오츠업을 반환합니다;
}
/*
* 엔진 설명자를 위한 메모리를 할당합니다. 저는
* 전역 변수를 사용하면 나중에 문제가 발생할 수 있기 때문입니다.
* 나중에 동일한 엔진의 인스턴스를 여러 개 만들기로 결정합니다.
* 첫날부터 안전한 편에 서는 것이 좋습니다...
*/
구조체 fs_engine *h = calloc(1, sizeof(*h));
if (h == NULL) {
엔진_이름을 반환합니다;
}
/*
* 엔진 API의 첫 번째 버전을 구현할 예정입니다.
* 멤캐시드 코어에 어떤 구조를 가져야 하는지 알려줘야 합니다.
* 예상
*/
h->engine.interface.interface = 1;
/*
* API 진입점을 이를 구현하는 함수에 매핑합니다.
*/
h->engine.initialize = fs_initialize;
h->engine.destroy = fs_destroy;
/* 핸들을 코어로 다시 전달합니다 */.
*핸들 = (ENGINE_HANDLE*)h;
엔진_성공을 반환합니다;
}
에서 제공하는 인터페이스가 create_instance
가 멤캐시드에서 지원되는 인터페이스에서 삭제되면 코어는 destroy()
를 즉시 사용합니다. 멤캐시드 코어는 절대로 any 엔진에서 반환된 포인터는 destroy()
를 호출합니다.
이제 계속해서 destroy()
함수입니다. 구현을 살펴보면 create_instance
에 매핑된 것을 볼 수 있습니다. destroy()
라는 함수에 fs_destroy()
:
/* 엔진 디스크립터에 할당된 메모리를 해제합니다 */.
free(handle);
}
코어가 우리가 지정한 인터페이스를 구현하는 경우 코어는 initialize()
메서드를 호출합니다. 이 때 엔진에서 데이터베이스 연결, 뮤텍스 초기화 등 모든 종류의 초기화를 수행해야 합니다. 엔진의 초기화
함수는 반환된 인스턴스 당 한 번만 호출됩니다. create_instance
(멤캐시드 코어가 여러 스레드를 사용하는 경우에도). 코어는 not 초기화 메서드가 반환되기 전에 API의 다른 함수를 호출할 수 없습니다.
현재로서는 어떤 종류의 초기화도 필요하지 않으므로 다음 초기화 코드를 사용할 수 있습니다:
const char* config_str) {
엔진_성공을 반환합니다;
}
엔진이 다음 이외의 다른 것을 반환하는 경우 ENGINE_SUCCESS
로 설정하면 멤캐시드 코어가 엔진 사용을 거부하고 destroy()
다음 블로그 글에서는 엔진을 로드하고 클라이언트의 명령을 처리할 수 있도록 기능을 추가해 보겠습니다.