2013년 4월 18일 목요일

Apache Avro IDL 을 이용한 RPC 구현

어제는 JSON 포맷의 Avro Protocol 파일을 정의하여, RPC 를 구현해 봤습니만 (Avro Protocol 방식),
오늘은 IDL을 이용하여 구현해 보도록 하겠습니다. IDL 이 json 포맷보다 훨씬 가독성이 좋아서, 저는 앞으로는 IDL로 구현해야겠네요.

오늘은 어제보다 좀 더 복잡한 코드를 만들어 보겠습니다. Avro IDL 관련 설명을 읽고 10분만에 구현했으니, 이 글을 보시는 분들도 단박에 이해 되실겁니다.

구현한 예제는 검색 서비스를 흉내낸 것인데,  데이터의 저장, 검색 함수를 지원합니다.

searchService.avdl (Avro IDL 포맷의 확장자는 avdl 입니다)

@namespace("example.avro.search")
protocol SearchService {
record Entity {
string rowId;
string @order("ascending") createdAt;
string text;
map<string> attrs;
}
record SearchResult {
int pageNo;
int pageSize;
int pageCount;
int totalItemCount;
array<Entity> entities;
}
string ping(); // return "PONG"
int persist(string id, Entity entity);
int persistAll(array<Entity> entities);
array<Entity> search(string queryString);
SearchResult search(string queryString, int pageNo = 1, int pageSize = 10);
}

SearchService 에는 Entity, SearchResult 라는 자료구조 (java에서는 class) 를 정의하고, 아래에는 rpc 메소드들을 정의했습니다.
JSON 포맷보다 훨씬 보기 좋죠?

이전 글에 설명되었듯이 idl-protocol 에 대해 source-generation 을 수행하고, 아래 코드를 작성하면, Search Service 를 제작할 수 있습니다.

SearchServer.java

package example.avro.search;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.avro.AvroRemoteException;
import org.apache.avro.ipc.NettyServer;
import org.apache.avro.ipc.NettyTransceiver;
import org.apache.avro.ipc.Server;
import org.apache.avro.ipc.specific.SpecificRequestor;
import org.apache.avro.ipc.specific.SpecificResponder;
import org.apache.avro.util.Utf8;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* Apache Avro IDL 을 이용한 RPC 예제입니다.
*
* @author sunghyouk.bae@gmail.com
* @since 13. 4. 18. 오후 3:34
*/
@Slf4j
public class SearchServer {
public static class SearchServiceImpl implements SearchService {
@Override
public CharSequence ping() throws AvroRemoteException {
return new Utf8("PONG");
}
@Override
public int persist(CharSequence id, Entity entity) throws AvroRemoteException {
System.out.printf("\nPersist entity. id=%s, entity=%s", id, entity);
return 1;
}
@Override
public int persistAll(List<Entity> entities) throws AvroRemoteException {
System.out.printf("\nPersist entities. entity count=%d", entities.size());
return entities.size();
}
@Override
public SearchResult search(CharSequence queryString, int pageNo, int pageSize) throws AvroRemoteException {
System.out.printf("\nsearch... queryString=%s, pageNo=%d, pageSize=%d", queryString, pageNo, pageSize);
// 검색된 엔티티들
List<Entity> entities = Lists.newArrayListWithCapacity(pageSize);
Map<String, String> attrs = Maps.newHashMap();
attrs.put("SR-NO", "00001");
attrs.put("SR-TYPE", "장비 불량");
for (int i = 0; i < pageSize; i++) {
entities.add(createSampleEntity(i, attrs));
}
return SearchResult.newBuilder()
.setPageNo(pageNo)
.setPageSize(pageSize)
.setPageCount(12)
.setTotalItemCount(pageSize * 12)
.setEntities(entities)
.build();
}
}
public static Entity createSampleEntity(int i, Map attrs) {
return Entity.newBuilder()
.setRowId("ROW-" + i)
.setCreatedAt(new Date().toString())
.setText("entity")
.setAttrs(attrs)
.build();
}
private static Server searchServer;
private static final int SEARCH_SERVER_PORT = 65123;
private static void startServer() throws IOException {
searchServer = new NettyServer(new SpecificResponder(SearchService.class, new SearchServiceImpl()),
new InetSocketAddress("localhost", SEARCH_SERVER_PORT));
}
public static void main(String[] args) throws Exception {
System.out.println("Starting server...");
startServer();
System.out.println("Server started");
NettyTransceiver searchClient = new NettyTransceiver(new InetSocketAddress("localhost", SEARCH_SERVER_PORT));
SearchService searchService = (SearchService) SpecificRequestor.getClient(SearchService.class, searchClient);
System.out.println("Client built, get proxy");
System.out.println("ping()=> " + searchService.ping());
Map<String, String> attrs = Maps.newHashMap();
attrs.put("SR-NO", "00001");
attrs.put("SR-TYPE", "장비 불량");
searchService.persist("ROW-1", createSampleEntity(1, attrs));
List<Entity> entities = Lists.newArrayListWithCapacity(1000);
for (int i = 0; i < 1000; i++) {
entities.add(createSampleEntity(i, attrs));
}
int count = searchService.persistAll(entities);
System.out.println("\nPersist count=" + count);
SearchResult searchResult = searchService.search("en*", 1, 10);
System.out.println("\nSearchResult=" + searchResult);
searchClient.close();
searchServer.close();
}
}
Client, Server 구현은 이전글의 예제와 거의 유사합니다.
아주 초간단하게 작성했지만 될 건 다 됩니다^^

이제 Avro 의 좀 더 깊은 영역을 살펴봐야겠습니다.

댓글 없음: