ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CouchBase 사용하기 ( part.1)
    NoSql/couchbase 2019. 7. 15. 10:27
    반응형

    CouchBase 를 사용 하기 위해선 생각보다 많은 준비가 필요하지 않다 . maven 으로 라이브러리를 추가해주고 클러스터와 버킷을 가져오는 부분을 구현해주면 된다. 

     

    기본적으로 카우치 베이스는 비동기식 처리로 진행 된다. 데이터를 넣은뒤 다시 조회시 타이밍에 따라 입력된 데이터가 조회 되지 않을수 있다.  공식사이트에서 sleep 처리로 그런 타이밍을 맞춰줘도 된다는 문장을 본기억이 있는데 일부 개발자들은 그런 방식을 선호 하지 않는경우도 있었다. 

     Async 와 Sync 방식을 전부 지원은 된다고 하지만 확실히 동기식 처리는 제대로 처리 되지 않는거 같고, 얼마전에 카우치베이스에서 한국에 기술 세미나를 열었는데 ( 참가.. 신청했고 참가신청번호 2번이였는데 ㅠㅠ.. 약속된 일정때문에 못갔다 천추의 한... ) 거기서 나온 내용중 그동안 Nosql 진영에서 몽고DB 만 거의 유일하게 지원하던 트랜잭션을 카우치베이스 6.5 버전 부터 지원한다고 하니 나중에 버전업을 해서 사용 해 볼 법하다. ( 현재 커뮤니티 버전은 6.0 버전이다. ) 

     

     일단 범용적으로 사용하기 위해서 일종의 유틸 모듈을만들었다. spring boot 에서 사용을 많이 할것 같아 spring framework 에 의존성을 가지게 만들었다. 

    package com.iparking.backend.couchbase;
    
    
    import com.couchbase.client.core.message.internal.PingReport;
    import com.couchbase.client.java.Bucket;
    import com.couchbase.client.java.Cluster;
    import com.couchbase.client.java.CouchbaseCluster;
    import com.couchbase.client.java.document.JsonDocument;
    import com.couchbase.client.java.document.json.JsonArray;
    import com.couchbase.client.java.document.json.JsonObject;
    import com.couchbase.client.java.query.N1qlQuery;
    import com.couchbase.client.java.query.N1qlQueryResult;
    import com.couchbase.client.java.query.N1qlQueryRow;
    import com.couchbase.client.java.view.ViewQuery;
    import com.couchbase.client.java.view.ViewResult;
    import com.couchbase.client.java.view.ViewRow;
    import com.google.gson.Gson;
    import com.google.gson.internal.LinkedTreeMap;
    import org.json.simple.JSONObject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PreDestroy;
    import java.util.*;
    
    @Service
    public class CouchService {
        Logger log = LoggerFactory.getLogger("CouchService");
    
        private CouchBaseConfig config = null;
    
        private Bucket master = null;
        private Bucket slave = null;
        private Cluster masterCluster = null;
        private Cluster slaveCluster = null;
    
        @Autowired
        public CouchService(final CouchBaseConfig config) {
            this.config = config;
            System.setProperty("com.couchbase.queryEnabled", "true");
            //connect to the cluster and open the configured bucket
            this.masterCluster = CouchbaseCluster.create(config.getMasterNodes());
            this.master = masterCluster.openBucket(config.getMasterBucket(), config.getMasterPassword());
    
            this.slaveCluster = CouchbaseCluster.create(config.getSlaveNodes());
            this.slave = slaveCluster.openBucket(config.getSlaveBucket(), config.getSlavePassword());
        }
    
        @PreDestroy
        public void preDestroy() {
            if (this.masterCluster != null) {
                this.masterCluster.disconnect();
            }
    
            if (this.slaveCluster != null) {
                this.slaveCluster.disconnect();
            }
        }
        private Bucket getBucket(){
    
            PingReport ping = master.ping();
            Gson gson = new Gson();
            JSONObject o = gson.fromJson( ping.exportToJson(), JSONObject.class );
            System.out.println(o);
            LinkedTreeMap services = (LinkedTreeMap) o.get("services");
            ArrayList views = (ArrayList) services.get("view");
            int errCnt = 0 ;
            for (int i = 0; i < views.size() ; i++) {
                Map tmp = (Map) views.get(i);
                String state = (String) tmp.get("state");
                if( state.equals("ok")){
    
                }else {
                    errCnt++;
                }
            }
    
            if( errCnt > 0  ){
                System.out.println("slave !");
                return slave;
            }else {
                System.out.println("master");
                return master;
            }
        }
    
        public JsonDocument insert(String key, JSONObject contents) {
            JsonObject contents1 = JsonObject.create().getObject(contents.toString());
            JsonDocument doc = JsonDocument.create(
                    key
                    , contents1
            );
            try {
    
    
                JsonDocument insertResult = master.insert(doc);
                log.info("insertResult:" + insertResult);
                return insertResult;
            } catch (Exception e) {
               e.printStackTrace();
               return null;
            }
    
    
        }
    
    
        public JsonDocument upsert(String key, JSONObject contents) {
            Bucket bucket = getBucket();
    
            JsonObject contents1 = JsonObject.create().getObject(contents.toString());
            JsonDocument doc = JsonDocument.create(
                    key
                    , contents1
            );
    
            try {
                System.setProperty("com.couchbase.queryEnabled", "true");
                //connect to the cluster and open the configured bucket
                JsonDocument insertResult = bucket.upsert(doc);
                log.info("insertResult:" + insertResult);
                return insertResult;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
    
    
    
        /**
         * 미사용 메서드 , 카우치베이스 조회 테스트를 위해 샘플용으로 만들어 놓음.
         *
         * @param design
         * @param view
         */
        public ViewResult findRange(String design, String view, String start, String end) {
            /**
             * Map-reduce 를 사용할때 Range 검색 코드
             */
             Bucket bucket = getBucket();
             ViewResult result =  bucket.query(ViewQuery
             .from(design,view)
             .startKey(JsonArray.from(start,null))
             .endKey(JsonArray.from(end, null)))
             ;
             for( ViewRow row : result){
               log.debug(row.toString());
               log.debug(row.document().content().toString());
             }
             return result;
        }
        /**
         * N1QL 을 사용하는 쿼리
         */
        public List<N1qlQueryRow> n1ql(String statement, HashMap<String,Object> params) {
            try {
                Bucket bucket = getBucket();
                System.out.println(statement);
                JsonObject placeHolders = JsonObject.create();
    
                Set<String> keys = params.keySet();
                Iterator it = keys.iterator();
    
                while(it.hasNext()){
                    String key = (String) it.next();
                    placeHolders.put(key , params.get(key) );
                }
    
                N1qlQuery query = N1qlQuery.parameterized(statement, placeHolders);
                N1qlQueryResult result = bucket.query(query);
                if (result.status().equals("success")){
                    List<N1qlQueryRow> tmp = result.allRows();
    //                for(N1qlQueryRow row : tmp){
    //                    log.debug(String.valueOf(row.value().getObject("data")));
    //                }
                    return tmp;
    
                }else {
                    log.error("CouchBaseException - "+result.errors().get(0) );
                    return null;
                }
            }catch(Exception e){
                log.error("CouchBaseException",e);
                return  null;
            }
        }
    }
    

     

    기본적으로 클러스터 두가지를 동시 접속 하며, 기능을 사용할때 ping() 을 사용하여 클러스터 상태가 좋으면 기존 master 로 붙어서 처리하고 master 클러스터에 한곳의 노드라도 상태가 이상할경우 slave 클러스터를 사용하도록 변경했다. ( master 와 slave 는 기본 서로 XDCR 을 이용한 데이터 동기화 환경으로 구성 했다고 가정한상태에서 사용하는 방법 ) 

     간단하게 insert , upsert , Range 검색 , N1QL 사용 에 대한 기능만 구현 했다. 로직상으로는 위와 같이 구성하고 환경구성을 따로 또 해줘야하는데 아직도 이것을 어떻게 구성하는게 간단할지 고민이 많다.  필요에 따라 라이브러리화 해야 한다면 프로젝트의 의존성을 가지지 않게 구성하는것도 충분히 생각해봐야 할 문제 같다. 

    package com.iparking.backend.couchbase;
    
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Configuration
    public class CouchBaseConfig  {
    
        @Value("${couchbase.master.nodes}")
        private List<String> masterNodes = new ArrayList<String>();
    
        @Value("${couchbase.slave.nodes}")
        private List<String> slaveNodes = new ArrayList<String>();
    
        @Value("${couchbase.master.bucket}")
        private String master;
    
        @Value("${couchbase.slave.bucket}")
        private String slave;
    
        @Value("${couchbase.master.password}")
        private String masterPwd;
    
        @Value("${couchbase.slave.password}")
        private String slavePwd;
    
        public List<String> getMasterNodes() {
            return masterNodes;
        }
        public List<String> getSlaveNodes() {
            return masterNodes;
        }
    
        public String getMasterBucket() {
            return master;
        }
        public String getSlaveBucket() {
            return slave;
        }
    
        public String getMasterPassword() {
            return masterPwd;
        }
    
        public String getSlavePassword() {
            return slavePwd;
        }
    
    }
    

     

    /* Maven 설정 */
    <dependency>
                <groupId>com.couchbase.client</groupId>
                <artifactId>java-client</artifactId>
                <version>2.7.2</version>
     </dependency>
    /* YML 설정 */
    couchbase:
      slave :
        bucket: [Bucket 이름]
        password: [접속 비밀 번호]
        nodes: [클러스터 IP 1], [클러스터 IP 2],[클러스터 IP 3]
      master :
        bucket: [Bucket 이름]
        password: [접속 비밀 번호]
        nodes: [클러스터 IP 1], [클러스터 IP 2],[클러스터 IP 3]

     

    반응형

    'NoSql > couchbase' 카테고리의 다른 글

    Couchbase  (1) 2019.05.03
Designed by Tistory.