From 556e48117c904c8a9b0f473be1ad7c1bccb5ce18 Mon Sep 17 00:00:00 2001 From: hjyun Date: Fri, 13 Mar 2020 13:28:51 +0900 Subject: [PATCH 1/4] CLEANUP: convert log4j configuration file format from properties to xml --- src/test/resources/log4j2.properties | 55 ------------------------- src/test/resources/log4j2.xml | 61 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 55 deletions(-) delete mode 100644 src/test/resources/log4j2.properties create mode 100644 src/test/resources/log4j2.xml diff --git a/src/test/resources/log4j2.properties b/src/test/resources/log4j2.properties deleted file mode 100644 index 998a7ab9c..000000000 --- a/src/test/resources/log4j2.properties +++ /dev/null @@ -1,55 +0,0 @@ -appender.console.type=Console -appender.console.name=STDOUT -appender.console.layout.type=PatternLayout -appender.console.layout.pattern=%d{yyyy-MM-dd HH:mm:ss} [%-5p](%-35c{1}:%-3L) %m%n - -rootLogger.level=WARN -rootLogger.appenderRef.stdout.ref=STDOUT - -loggers=StatisticsHandler,ArcusClient - -logger.StatisticsHandler.name=net.spy.memcached.StatisticsHandler -logger.StatisticsHandler.level=INFO - -logger.ArcusClient.name=net.spy.memcached.ArcusClient -logger.ArcusClient.level=INFO - -# uncomment below to debug -#logger.BTreeGetBulkOperationImpl.name=net.spy.memcached.protocol.ascii.BTreeGetBulkOperationImpl -#logger.BTreeGetBulkOperationImpl.level=DEBUG -# -#logger.CollectionStoreOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionStoreOperationImpl -#logger.CollectionStoreOperationImpl.level=DEBUG -# -#logger.CollectionPipedStoreOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionPipedStoreOperationImpl -#logger.CollectionPipedStoreOperationImpl.level=DEBUG -# -#logger.CollectionGetOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionGetOperationImpl -#logger.CollectionGetOperationImpl.level=DEBUG -# -#logger.BTreeSortMergeGetOperationImpl.name=net.spy.memcached.protocol.ascii.BTreeSortMergeGetOperationImpl -#logger.BTreeSortMergeGetOperationImpl.level=DEBUG -# -#logger.CollectionDeleteOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionDeleteOperationImpl -#logger.CollectionDeleteOperationImpl.level=DEBUG -# -#logger.CollectionUpdateOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionUpdateOperationImpl -#logger.CollectionUpdateOperationImpl.level=DEBUG -# -#logger.CollectionPipedExistOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionPipedExistOperationImpl -#logger.CollectionPipedExistOperationImpl.level=DEBUG -# -#logger.SetAttrOperationImpl.name=net.spy.memcached.protocol.ascii.SetAttrOperationImpl -#logger.SetAttrOperationImpl.level=DEBUG -# -#logger.MemcachedConnection.name=net.spy.memcached.MemcachedConnection -#logger.MemcachedConnection.level=ERROR -# -#logger.CollectionUpsertOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionUpsertOperationImpl -#logger.CollectionUpsertOperationImpl.level=DEBUG -# -#logger.StoreOperationImpl.name=net.spy.memcached.protocol.ascii.StoreOperationImpl -#logger.StoreOperationImpl.level=DEBUG -# -#logger.CollectionCountOperationImpl.name=net.spy.memcached.protocol.ascii.CollectionCountOperationImpl -#logger.CollectionCountOperationImpl.level=DEBUG diff --git a/src/test/resources/log4j2.xml b/src/test/resources/log4j2.xml new file mode 100644 index 000000000..8cdb98499 --- /dev/null +++ b/src/test/resources/log4j2.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 2ddc1bb9a5be0250a47755508b96e505fe75a090 Mon Sep 17 00:00:00 2001 From: hjyun Date: Fri, 13 Mar 2020 14:15:32 +0900 Subject: [PATCH 2/4] DOC: fix broken link of flush in other API --- docs/09-other-API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/09-other-API.md b/docs/09-other-API.md index fe801fc30..0d4935e94 100644 --- a/docs/09-other-API.md +++ b/docs/09-other-API.md @@ -2,7 +2,7 @@ 본 절에서는 아래의 나머지 API들을 설명한다. -- [Flush](08-other-API.md#flush) +- [Flush](09-other-API.md#flush) ### Flush From b4cd353e54907122a4ea7e2baae3a117d14b06b7 Mon Sep 17 00:00:00 2001 From: hjyun Date: Fri, 13 Mar 2020 14:15:46 +0900 Subject: [PATCH 3/4] DOC: remove bold on link in btree API --- docs/07-btree-API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/07-btree-API.md b/docs/07-btree-API.md index 7026a49ba..af0ce489a 100644 --- a/docs/07-btree-API.md +++ b/docs/07-btree-API.md @@ -8,7 +8,7 @@ B+tree item은 하나의 key에 대해 b+tree 구조 기반으로 b+tree key(bke - 하나의 b+tree 내에서 모든 element는 동일한 bkey 유형을 가져야 한다. 즉, long bkey 유형과 byte array bkey 유형이 혼재할 수 없다. -B+tree item 구조와 기본 특징은 **[Arcus Server Ascii Protocol 문서의 내용](https://github.com/naver/arcus-memcached/blob/master/doc/arcus-collection-concept.md)**을 +B+tree item 구조와 기본 특징은 [Arcus Server Ascii Protocol 문서의 내용](https://github.com/naver/arcus-memcached/blob/master/doc/arcus-collection-concept.md)을 먼저 참고하기 바란다. B+tree item 연산의 설명에 앞서, b+tree 조회 및 변경에 사용하는 객체들을 설명한다. From ff47286e38eeb054afc46399f84f025a7f5e131c Mon Sep 17 00:00:00 2001 From: hjyun Date: Fri, 13 Mar 2020 15:06:33 +0900 Subject: [PATCH 4/4] DOC: update description of logging --- docs/02-arcus-java-client.md | 254 +++++++++------ docs/arcus-java-client-getting-started.md | 368 +++++++++++----------- 2 files changed, 333 insertions(+), 289 deletions(-) diff --git a/docs/02-arcus-java-client.md b/docs/02-arcus-java-client.md index 0fe752a07..ba0ac5566 100644 --- a/docs/02-arcus-java-client.md +++ b/docs/02-arcus-java-client.md @@ -1,4 +1,4 @@ -## Arcus Java Client +## Arcus Java Client - [Arcus Client 기본 사용법](02-arcus-java-client.md#arcus-client-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95) - [Arcus Client 생성, 소멸, 관리](02-arcus-java-client.md#arcus-client-%EC%83%9D%EC%84%B1-%EC%86%8C%EB%A9%B8-%EA%B4%80%EB%A6%AC) @@ -22,47 +22,47 @@ import net.spy.memcached.ConnectionFactoryBuilder; public class HelloArcus { - private static final String ARCUS_ADMIN = "10.0.0.1:2181,10.0.0.2:2181,10.0.0.3:2181"; - private static final String SERVICE_CODE = "test"; - private final ArcusClient arcusClient; - - public static void main(String[] args) { - HelloArcus hello = new HelloArcus(); - System.out.printf("hello.setTest() result=%b", hello.setTest()); - hello.closeArcusConnection(); - } - - public HelloArcus() { - arcusClient = ArcusClient.createArcusClient(ARCUS_ADMIN, SERVICE_CODE, - new ConnectionFactoryBuilder()); // (1) - } - - public boolean setTest() { - Future future = null; - try { - future = arcusClient.set("sample:testKey", 10, "testValue"); // (2) - } catch (IllegalStateException e) { - // client operation queue 문제로 요청이 등록되지 않았을 때 예외처리. - } - - if (future == null) return false; - - try { - return future.get(500L, TimeUnit.MILLISECONDS); // (3) - } catch (TimeoutException te) { // (4) - future.cancel(true); - } catch (ExecutionException re) { // (5) - future.cancel(true); - } catch (InterruptedException ie) { // (6) - future.cancel(true); - } - - return false; - } - - public void closeArcusConnection() { - arcusClient.shutdown(); // (7) - } + private static final String ARCUS_ADMIN = "10.0.0.1:2181,10.0.0.2:2181,10.0.0.3:2181"; + private static final String SERVICE_CODE = "test"; + private final ArcusClient arcusClient; + + public static void main(String[] args) { + HelloArcus hello = new HelloArcus(); + System.out.printf("hello.setTest() result=%b", hello.setTest()); + hello.closeArcusConnection(); + } + + public HelloArcus() { + arcusClient = ArcusClient.createArcusClient(ARCUS_ADMIN, SERVICE_CODE, + new ConnectionFactoryBuilder()); // (1) + } + + public boolean setTest() { + Future future = null; + try { + future = arcusClient.set("sample:testKey", 10, "testValue"); // (2) + } catch (IllegalStateException e) { + // client operation queue 문제로 요청이 등록되지 않았을 때 예외처리. + } + + if (future == null) return false; + + try { + return future.get(500L, TimeUnit.MILLISECONDS); // (3) + } catch (TimeoutException te) { // (4) + future.cancel(true); + } catch (ExecutionException re) { // (5) + future.cancel(true); + } catch (InterruptedException ie) { // (6) + future.cancel(true); + } + + return false; + } + + public void closeArcusConnection() { + arcusClient.shutdown(); // (7) + } } ``` @@ -244,126 +244,172 @@ ArcusClient client = ArcusClient.createArcusClient(SERVICE_CODE, cfb); ##### Logger 설정 -Arcus client 사용 시에 ArcusClient 자체 logger(DefaultLogger), log4j, slf4j, JDK logger 등 -4가지 종류의 Logger를 사용할 수 있다. +Arcus client 사용 시에 default(DefaultLogger), log4j(Log4JLogger), slf4j(SLF4JLogger), jdk(SunLogger) 등 4가지 종류의 Logger를 사용할 수 있다. 사용할 logger를 지정하지 않으면 ArcusClient는 DefaultLogger를 기본으로 사용하며, DefaultLogger는 INFO level 이상의 로그를 stderr (System.err) 로 출력한다. (변경 불가) -log4j를 사용하여 ArcusClient 로그를 관리하려면, 아래 옵션을 WAS나 자바 프로세스 옵션에 추가하여 -JVM 구동시 System property를 지정한다. (log4j 라이브러리가 클래스 패스에 있어야 오류가 발생하지 않는다.) +log4j를 사용하여 ArcusClient 로그를 관리하려면, 아래 옵션을 WAS나 자바 프로세스 옵션에 추가하여 JVM 구동시 System property를 지정한다. + ``` -Dnet.spy.log.LoggerImpl=net.spy.memcached.compat.log.Log4JLogger ``` + 또는, 소스 코드에서 ArcusClient / ArcusClientPool을 사용하기 전에 직접 System property를 설정하여 사용할 수 있다. (programmatic configuration) ```java -System.setProperty(“net.spy.log.LoggerImpl”, “net.spy.memcached.compat.log.Log4JLogger”); +System.setProperty("net.spy.log.LoggerImpl", "net.spy.memcached.compat.log.Log4JLogger"); ... ConnectionFactoryBuilder cfb = new ConnectionFactoryBuilder(); ArcusClient client = ArcusClient.createArcusClient(SERVICE_CODE, cfb); ``` + Arcus Java client에서는 Log를 기록할 때 Class의 이름(```clazz.getName()```)을 기준으로 Logger를 구분하여 사용하며, class의 이름과 정확히 일치하는 로거가 없다면 logger tree 상의 상위 logger 를 사용한다. 아래의 예제는 ```root``` logger 의 level을 ```WARN```으로 설정하여 WARN level 이상의 로그는 항상 기록하고, ```net.spy.memcached.protocol.ascii.CollectionUpdateOperationImpl``` class의 로그만 DEBUG level 이상의 로그를 기록하도록 한 예제이다. ```xml - - - - - - - - + + + + + + ``` Application을 디버깅해야 할 때 Arcus client에서 Arcus server로 전송하는 ascii protocol 문자열이 궁금할 때가 있다. Arcus Java Client에서 Arcus server로 전송하는 protocol을 로그로 살펴보려면 아래와 같이 logger를 설정하면 된다. 예제에 나열된 logger를 모두 설정하면 요청(get, set 등..)별로 모든 로그가 남게 되니 필요한 요청에 해당하는 logger만 설정하면 편리하다. Ascii Protocol에 대한 자세한 내용은 [Arcus 서버 명령 프로토콜](https://github.com/naver/arcus-memcached/blob/master/doc/arcus-ascii-protocol.md) 문서를 참고하기 바란다. ```xml - - - - + + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + - - - - + + + +``` + +기타 log4j의 자세한 설정 방법은 [log4j 설정 방법](http://logging.apache.org/log4j/2.x/manual/configuration.html)을 확인하기 바란다. + +##### Log4JLogger 사용시 유의사항 + +log4j 1.2 이하 버전에서 보안 취약점이 존재하여, Arcus client의 1.11.5 버전부터 Log4JLogger를 사용하려면 log4j2 라이브러리가 요구된다. 이를 위해 응용 의존성에 아래와 같이 log4j2 라이브러리를 추가한다. + +```xml + + org.apache.logging.log4j + log4j-core + 2.8.2 + + + org.apache.logging.log4j + log4j-api + 2.8.2 + ``` -기타 log4j의 자세한 설정 방법은 [log4j 설정 방법](http://logging.apache.org/log4j/1.2/manual.html)을 확인하기 바란다. +만약 아래와 같은 예외가 발생되면, log4j2 라이브러리가 클래스패스에 존재하지 않은 것이다. log4j2 라이브러리가 응용 의존성에 제대로 추가가 됐는지 확인하도록 한다. + +``` +Warning: net.spy.memcached.compat.log.Log4JLogger not found while initializing net.spy.compat.log.LoggerFactory +java.lang.NoClassDefFoundError: org/apache/logging/log4j/spi/ExtendedLogger + at java.base/java.lang.Class.forName0(Native Method) + at java.base/java.lang.Class.forName(Class.java:315) + at net.spy.memcached.compat.log.LoggerFactory.getConstructor(LoggerFactory.java:134) + at net.spy.memcached.compat.log.LoggerFactory.getNewInstance(LoggerFactory.java:119) + at net.spy.memcached.compat.log.LoggerFactory.internalGetLogger(LoggerFactory.java:100) + at net.spy.memcached.compat.log.LoggerFactory.getLogger(LoggerFactory.java:89) + at net.spy.memcached.ArcusClient.(ArcusClient.java:183) + at Main.main(Main.java:10) + ``` ##### SLF4JLogger 사용시 유의 사항 -slf4j와 호환되는 log4j 이외의 라이브러리(logback, log4j2, ...)를 쓸 경우, net.spy.memcached.compat.log.SLF4JLogger 클래스를 사용할 것이다. 이 클래스를 사용하기 전에 필수적으로 해야 하는 작업이 있다. (SLF4JLogger와 log4j를 조합해서 사용한다면 하지 않아도 된다.) +slf4j를 사용하는 경우, Arcus client의 SLF4JLogger 클래스를 사용할 것이다. 이 클래스를 사용하려면 slf4j를 구현한 로깅 라이브러리가 응용 의존성에 추가되어야 한다. 만약 추가하지 않을 경우 아래의 예외 메시지가 발생한다. -ArcusClient는 기본적으로 Zookeeper에 의해서 slf4j의 구현 라이브러리인 slf4j-log4j12를 기본 dependency로 가진다. 따라서 log4j 이외의 라이브러리를 SLF4JLogger와 조합해서 사용하려면 ArcusClient dependency의 exclusion에 slf4j-log4j12를 추가해야 한다. +``` +SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". +SLF4J: Defaulting to no-operation (NOP) logger implementation +SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. +``` -예를 들어 ArcusClient 사용자가 SLF4JLogger와 logback을 조합해서 사용할 경우 dependency 설정을 다음과 같이 해야 한다. +log4j, logback과 같은 대표적인 자바의 로그 라이브러리들은 slf4j api를 구현한 구현 라이브러리를 제공하고 있다. 해당 라이브러리를 사용할 경우 아래와 같이 응용 의존성에 추가하도록 한다. 자세한 내용은 [slf4j](http://www.slf4j.org/manual.html#swapping) 문서를 참고한다. +```xml + + + com.navercorp.arcus + arcus-java-client + ${arcus-java-client.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-slf4j-impl + ${log4j.version} + ``` + +```xml + com.navercorp.arcus arcus-java-client ${arcus-java-client.version} - - - org.slf4j - slf4j-log4j12 - - - ch.qos.logback - logback-classic - ${logback.version} + ch.qos.logback + logback-classic + ${logback.version} - ch.qos.logback - logback-core - ${logback.version} + ch.qos.logback + logback-core + ${logback.version} ``` -2개 이상의 slf4j의 구현 라이브러리(logback-classic, slf4j-log4j12, ...)들이 같은 classpath에 존재할 경우, SLF4J에서 [multiple binding error](http://www.slf4j.org/codes.html#multiple_bindings)가 발생하므로 반드시 exclusion 키워드를 이용해 slf4j 구현 라이브러리가 하나만 존재하도록 하여야 한다. +또한 2개 이상의 slf4j의 구현 라이브러리(log4j-slf4j-impl, logback-classic, ...)들이 같은 클래스패스에 존재할 경우, SLF4J에서 [multiple binding error](http://www.slf4j.org/codes.html#multiple_bindings)가 발생하므로 반드시 exclusion 키워드를 이용해 slf4j 구현 라이브러리가 하나만 존재하도록 하여야 한다. ``` SLF4J: Class path contains multiple SLF4J bindings. diff --git a/docs/arcus-java-client-getting-started.md b/docs/arcus-java-client-getting-started.md index 9fa82247d..3e29e7d17 100644 --- a/docs/arcus-java-client-getting-started.md +++ b/docs/arcus-java-client-getting-started.md @@ -20,15 +20,15 @@ Arcus는 오픈소스 key-value 캐시 서버인 memcached를 기반으로 부 ### 미리 알아두기 - 키(key) - - Arcus의 key는 prefix와 subkey로 구성되며, prefix와 subkey는 콜론(:)으로 구분됩니다. (예) *users:user_12345* - - Arcus는 prefix를 기준으로 별도의 통계를 수집합니다. prefix 개수의 제한은 없으나 통계 수집을 하는 경우에는 너무 많지 않는 수준(5~10개)으로 생성하시는 것을 권합니다. - - 키는 prefix, subkey를 포함하여 250자를 넘을 수 없습니다. 따라서 반드시 응용에서 키 길이를 제한하셔야 합니다. + - Arcus의 key는 prefix와 subkey로 구성되며, prefix와 subkey는 콜론(:)으로 구분됩니다. (예) *users:user_12345* + - Arcus는 prefix를 기준으로 별도의 통계를 수집합니다. prefix 개수의 제한은 없으나 통계 수집을 하는 경우에는 너무 많지 않는 수준(5~10개)으로 생성하시는 것을 권합니다. + - 키는 prefix, subkey를 포함하여 250자를 넘을 수 없습니다. 따라서 반드시 응용에서 키 길이를 제한하셔야 합니다. - 값(value) - - 하나의 키에 대한 값은 바이트 스트림 형태로 최대 1MB 까지 저장될 수 있습니다. - - 자바 객체를 저장하는 경우, 해당 객체는 반드시 Serializable 인터페이스를 구현해야 합니다. + - 하나의 키에 대한 값은 바이트 스트림 형태로 최대 1MB 까지 저장될 수 있습니다. + - 자바 객체를 저장하는 경우, 해당 객체는 반드시 Serializable 인터페이스를 구현해야 합니다. * Arcus 접속 정보 - - Arcus admin: ZooKeeper 서버 주소로서 캐시 서버들의 IP와 PORT 정보를 조회하고 변경이 있을 때 클라이언트에게 알려주는 역할을 합니다. - - Arcus service code: 사용자 또는 서비스에게 할당된 캐시 서버들을 구분짓는 코드값입니다. + - Arcus admin: ZooKeeper 서버 주소로서 캐시 서버들의 IP와 PORT 정보를 조회하고 변경이 있을 때 클라이언트에게 알려주는 역할을 합니다. + - Arcus service code: 사용자 또는 서비스에게 할당된 캐시 서버들을 구분짓는 코드값입니다. ### Hello, Arcus! @@ -47,54 +47,54 @@ $ mvn eclipse:eclipse // 이클립스 IDE를 사용하는 경우 실행하여 ```xml - 4.0.0 - - com.navercorp.arcus - arcus-quick-start - 1.0-SNAPSHOT - jar - - arcus-quick-start - http://maven.apache.org - - - UTF-8 - - - - - - junit - junit - 4.4 - test - - - - - com.navercorp.arcus - arcus-java-client - 1.8.0 - - - - - log4j - log4j - 1.2.16 - - - org.slf4j - slf4j-api - 1.6.1 - - - org.slf4j - slf4j-log4j12 - 1.6.1 - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + com.navercorp.arcus + arcus-quick-start + 1.0-SNAPSHOT + jar + + arcus-quick-start + http://maven.apache.org + + + UTF-8 + + + + + + junit + junit + 4.4 + test + + + + + com.navercorp.arcus + arcus-java-client + 1.11.5 + + + + + org.apache.logging.log4j + log4j-core + 2.8.2 + + + org.apache.logging.log4j + log4j-api + 2.8.2 + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.8.2 + + ``` @@ -116,18 +116,18 @@ import org.junit.Test; public class HelloArcusTest { - HelloArcus helloArcus = new HelloArcus("127.0.0.1:2181", "test"); - - @Before - public void sayHello() { - helloArcus.sayHello(); - } - - @Test - public void listenHello() { - Assert.assertEquals("Hello, Arcus!", helloArcus.listenHello()); - } - + HelloArcus helloArcus = new HelloArcus("127.0.0.1:2181", "test"); + + @Before + public void sayHello() { + helloArcus.sayHello(); + } + + @Test + public void listenHello() { + Assert.assertEquals("Hello, Arcus!", helloArcus.listenHello()); + } + } ``` @@ -143,129 +143,127 @@ import net.spy.memcached.ConnectionFactoryBuilder; public class HelloArcus { - private String arcusAdmin; - private String serviceCode; - private ArcusClient arcusClient; - - public HelloArcus(String arcusAdmin, String serviceCode) { - this.arcusAdmin = arcusAdmin; - this.serviceCode = serviceCode; - - // log4j logger를 사용하도록 설정합니다. - // 코드에 직접 추가하지 않고 아래의 JVM 환경변수를 사용해도 됩니다. - // -Dnet.spy.log.LoggerImpl=net.spy.memcached.compat.log.Log4JLogger - System.setProperty("net.spy.log.LoggerImpl", "net.spy.memcached.compat.log.Log4JLogger"); - - // Arcus 클라이언트 객체를 생성합니다. - // - arcusAdmin : Arcus 캐시 서버들의 그룹을 관리하는 admin 서버(ZooKeeper)의 주소입니다. - // - serviceCode : 사용자에게 할당된 Arcus 캐시 서버들의 집합에 대한 코드값입니다. - // - connectionFactoryBuilder : 클라이언트 생성 옵션을 지정할 수 있습니다. - // - // 정리하면 arcusAdmin과 serviceCode의 조합을 통해 유일한 캐시 서버들의 집합을 얻어 연결할 수 있는 것입니다. - this.arcusClient = ArcusClient.createArcusClient(arcusAdmin, serviceCode, new ConnectionFactoryBuilder()); - } - - public boolean sayHello() { - Future future = null; - boolean setSuccess = false; - - // Arcus의 "test:hello" 키에 "Hello, Arcus!"라는 값을 저장합니다. - // 그리고 Arcus의 거의 모든 API는 Future를 리턴하도록 되어 있으므로 - // 비동기 처리에 특화된 서버가 아니라면 반드시 명시적으로 future.get()을 수행하여 - // 반환되는 응답을 기다려야 합니다. - future = this.arcusClient.set("test:hello", 600, "Hello, Arcus!"); - - try { - setSuccess = future.get(700L, TimeUnit.MILLISECONDS); - } catch (Exception e) { - if (future != null) future.cancel(true); - e.printStackTrace(); - } - - return setSuccess; - } - - public String listenHello() { - Future future = null; - String result = "Not OK."; - - // Arcus의 "test:hello" 키의 값을 조회합니다. - // Arcus에서는 가능한 모든 명령에 명시적으로 timeout 값을 지정하도록 가이드 하고 있으며 - // 사용자는 set을 제외한 모든 요청에 async로 시작하는 API를 사용하셔야 합니다. - future = this.arcusClient.asyncGet("test:hello"); - - try { - result = (String)future.get(700L, TimeUnit.MILLISECONDS); - } catch (Exception e) { - if (future != null) future.cancel(true); - e.printStackTrace(); - } - - return result; - } + private String arcusAdmin; + private String serviceCode; + private ArcusClient arcusClient; + + public HelloArcus(String arcusAdmin, String serviceCode) { + this.arcusAdmin = arcusAdmin; + this.serviceCode = serviceCode; + + // log4j logger를 사용하도록 설정합니다. + // 코드에 직접 추가하지 않고 아래의 JVM 환경변수를 사용해도 됩니다. + // -Dnet.spy.log.LoggerImpl=net.spy.memcached.compat.log.Log4JLogger + System.setProperty("net.spy.log.LoggerImpl", "net.spy.memcached.compat.log.Log4JLogger"); + + // Arcus 클라이언트 객체를 생성합니다. + // - arcusAdmin : Arcus 캐시 서버들의 그룹을 관리하는 admin 서버(ZooKeeper)의 주소입니다. + // - serviceCode : 사용자에게 할당된 Arcus 캐시 서버들의 집합에 대한 코드값입니다. + // - connectionFactoryBuilder : 클라이언트 생성 옵션을 지정할 수 있습니다. + // + // 정리하면 arcusAdmin과 serviceCode의 조합을 통해 유일한 캐시 서버들의 집합을 얻어 연결할 수 있는 것입니다. + this.arcusClient = ArcusClient.createArcusClient(arcusAdmin, serviceCode, new ConnectionFactoryBuilder()); + } + + public boolean sayHello() { + Future future = null; + boolean setSuccess = false; + + // Arcus의 "test:hello" 키에 "Hello, Arcus!"라는 값을 저장합니다. + // 그리고 Arcus의 거의 모든 API는 Future를 리턴하도록 되어 있으므로 + // 비동기 처리에 특화된 서버가 아니라면 반드시 명시적으로 future.get()을 수행하여 + // 반환되는 응답을 기다려야 합니다. + future = this.arcusClient.set("test:hello", 600, "Hello, Arcus!"); + + try { + setSuccess = future.get(700L, TimeUnit.MILLISECONDS); + } catch (Exception e) { + if (future != null) future.cancel(true); + e.printStackTrace(); + } + + return setSuccess; + } + + public String listenHello() { + Future future = null; + String result = "Not OK."; + + // Arcus의 "test:hello" 키의 값을 조회합니다. + // Arcus에서는 가능한 모든 명령에 명시적으로 timeout 값을 지정하도록 가이드 하고 있으며 + // 사용자는 set을 제외한 모든 요청에 async로 시작하는 API를 사용하셔야 합니다. + future = this.arcusClient.asyncGet("test:hello"); + + try { + result = (String)future.get(700L, TimeUnit.MILLISECONDS); + } catch (Exception e) { + if (future != null) future.cancel(true); + e.printStackTrace(); + } + + return result; + } } ``` -#### src/test/resources/log4j.xml +#### src/test/resources/log4j2.xml ```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ``` #### 테스트