Mapped Statements collection does not contain value for XXX

이 Exception이 나지 않게 하기 위해서 처리해야 할 것들

 

1. Mapper 설정을 잘 확인하기

Spring Bean 설정을 살펴보며, 제대로 import 되었는지 체크해야 한다.

그리고, bean 설정의 SqlSessionFactory안의 configLocation 에 제대로 mybatis 설정을 제대로 지정하는지 확인한다.

 

<bean id="statSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="statDataSource" />
    <property name="configLocation" value="classpath:mybatis/stat-mybatis-config.xml" />
</bean>

 

2. 바인딩된 쿼리 문을 확인한다. 

오타로 인해서 쿼리문을 못찾는 경우가 있다. 

 

3.  mapper의 basePackage 설정을 확인한다. 유일무인 값을 지정해줘야 한다.

단, 이미 등록한 mybatis mapper 설정의 하위 디렉토리 이름도 넣지 않도록 한다. 무조건 에러 난다.

 

여러 개의 DB를 사용하는 경우 dao를 하나의 패키지안에 넣는 경우가 없도록 해야 한다.

 

이미 아래와 같은 설정이 bean 설정에 포함되어 있다면.

<bean id="statMapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.google.admin.stat" />
    <property name="sqlSessionFactoryBeanName" value="statSqlSessionFactory" />
</bean>

 

아래와 같은 설정은 Mybatis가 읽지 못한다.

<bean id="statAMapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.google.admin.stat.A" />
    <property name="sqlSessionFactoryBeanName" value="statSqlSessionFactory" />
</bean>

 

-> 패키지 이름을 따로 만들어주어야 제대로 동작한다.

<bean id="statAMapper" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.google.admin.statA" />
    <property name="sqlSessionFactoryBeanName" value="statSqlSessionFactory" />
</bean>


Posted by '김용환'
,


Hive-Java Thrift Client 연동은 약간의 문제가 있다. 버전 이슈가 크다.

Hadoop-Hive-lib Thrift 간의 궁합이 맞지 않으면, 계속 에러가 발생한다. 이 궁합의 버전으로 계속 테스트해보아도 잘 안맞아서 다양한 Exception과 Code 분석 끝에.. 요즘 나온 상위버전으로는 해결이 안된다는 사실을 파악했다. 오픈 소스의 가장 큰 단점은 이 호환성 문제인듯..


구글 검색해보니. 아래 분의 블로그가 나온다. 

http://odysseymoon.tistory.com/m/post/view/id/36

이 훌륭하신 분이 Hive-Thrift 궁합버전을 잘 정리해주셨다. 

(또한 Mybatis-Hive 코드는 신선함까지 전달된다. 쵝오~) 



Pom.xml 파일의 dependecy와 아래와 같이 설정하면 잘 동작 됨.


<properties>

<hadoop.version>2.0.0-cdh4.0.0</hadoop.version>

<hive.version>0.8.1-cdh4.0.0</hive.version>

</properties>

...

<repositories>

<repository>

<id>com.cloudera.cdh4</id>

<name>Cloudera CDH4 Maven Repository</name>

<url> https://repository.cloudera.com/artifactory/cloudera-repos</url>

</repository>

</repositories>


....

<dependency>

<groupId>org.apache.hadoop</groupId>

<artifactId>hadoop-common</artifactId>

<version>${hadoop.version}</version>

</dependency>

<dependency>

<groupId>org.apache.hive</groupId>

<artifactId>hive-common</artifactId>

<version>${hive.version}</version>

</dependency>

<dependency>

<groupId>org.apache.hive</groupId>

<artifactId>hive-jdbc</artifactId>

<version>${hive.version}</version>

</dependency>

<dependency>

<groupId>org.apache.hive</groupId>

<artifactId>hive-metastore</artifactId>

<version>${hive.version}</version>

</dependency>

<dependency>

<groupId>org.apache.hive</groupId>

<artifactId>hive-service</artifactId>

<version>${hive.version}</version>

</dependency>

<dependency>

<groupId>org.apache.hive</groupId>

<artifactId>hive-serde</artifactId>

<version>${hive.version}</version>

</dependency>

<dependency>

<groupId>org.apache.thrift</groupId>

<artifactId>libthrift</artifactId>

<version>0.6.1</version>

</dependency>




 Hive 서버쪽 설정 계정 확인 (hive는 metastore 저장을 위해서 mysql을 사용하고 있다.)

$ vi hive-site.xml 

<property>

  <name>javax.jdo.option.ConnectionURL</name>

  <value>jdbc:mysql://localhost:20306/hive?createDatabaseIfNotExist=true</value>

  <description>JDBC connect string for a JDBC metastore</description>

</property>


<property>

  <name>javax.jdo.option.ConnectionDriverName</name>

  <value>com.mysql.jdbc.Driver</value>

  <description>Driver class name for a JDBC metastore</description>

</property>


<property>

  <name>javax.jdo.option.ConnectionUserName</name>

  <value>hive</value>

  <description>username to use against metastore database</description>

</property>


<property>

  <name>javax.jdo.option.ConnectionPassword</name>

  <value>hive</value>

  <description>password to use against metastore database</description>

</property>


Hive-Thrift Server서버 실행

$ HIVE_PORT=20180 hive --service hiveserver &

[1] 111993

Starting Hive Thrift Server




Java Client 테스트 코드 



import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.SQLException;

import java.sql.Statement;


public class LanguageJob3 {

private static String driverName = "org.apache.hadoop.hive.jdbc.HiveDriver";


private static String tableName = "test";

/**

* @param args

* @throws SQLException

*/

public static void main(String[] args) throws SQLException {

try {

Class.forName(driverName);

} catch (ClassNotFoundException e) {

e.printStackTrace();

System.exit(1);

}

Connection con = DriverManager.getConnection("jdbc:hive://1.1.1.1:20180", "hive", "hive");

Statement stmt = con.createStatement();

// 1. drop table

stmt.executeQuery("drop table " + tableName);

// 2. create table

ResultSet res = stmt.executeQuery("create table " + tableName + " (key string, value string)");


// 3. describe table

String sql = "describe " + tableName;

   System.out.println("Running: " + sql);

   res = stmt.executeQuery(sql);

   while (res.next()) {

     System.out.println(res.getString(1) + "\t" + res.getString(2));

   }


// 4. select table

sql = "select * from " + tableName + " limit 1";

System.out.println("Running: " + sql);

res = stmt.executeQuery(sql);

while (res.next()) {

System.out.println(res.getString(1));

}

}

}



테스트 결과

Running: describe test

key string

value string

Running: select * from test limit 1

(데이터가 없으니 안나오는 게 맞음)



잘 실행됨. 



Posted by '김용환'
,

 

일반적으로 자바 웹 서비스는 cache(oscache나 ehcache)를 이용하여 성능을 높인다. 이 cache를 적용하면 메모리에 캐쉬 데이터가 쌓이게 된다.

 

* 일반적인 경우

jvm의 memory 관리 방식이 Generational Model로 설정되어 있는 경우, Young Gen이 조금씩 커지기 시작하고, Young Gen과 Survivor Gen 간의 여러 번의 작은 gc가 일어나서 old gen으로 승격이 일어나게 된다.

이런 old gen에서의 Full GC는 결국 부하를 일으키는 요인이 되고, Full GC 시간이 길어지면 그 시점에 접속한 웹 서비스들은 느려지거나 장애로 이어지게 된다.

이를 해결하기 위해서 일반 개발자들은 CMS 알고리즘으로 완벽히 무장하거나, Survivor ratio를 잘 조절하여 전체 Young Gen 크기를 넓히는 방법을 사용하기도 한다. (개인적으로는 후자를 선호)

 

 

그러나 사실 곰곰히 생각해보면..

Young Gen 안의 데이터가 최대한 쌓이지 않도록 조절해서 Young Gen안에서 자연스럽게 GC(minor gc)가 일어날 때, 자연스럽게 정리되는 것이 가장 좋다.

이 때 오랫동안 data가 저장되는 것은 DB (mybatis cache) 나 특정 주기에 가져오는 데이터들이다.

 

이 cache data들만 잘 분산한다면. 효율적이 될 수도 있겠다 생각이 들어서.. 특이한 실험을 했다.

하나의 물리장비에 단일 JVM과 단일 Cache Daemon (redis 또는 memcached) 을 사용했다. 참고로 cache hit는 70% 정도 되었다.

 

 

ehcache나 oscache에 비해서 io 부하(tcp/ip)가 있다는 부담감이 있었지만. 사용해보니 만족스러웠다.

물론 성능쪽 이득도 있지만, 가장 좋은 것은 jvm의 메모리 사용량이었다.

 

Spring Cache를 이용해서 ehcache binding 했을 때는 이런 식의 Heap Graph를 볼 수 있었다.

 

 

 

그러나 cache daemon을 따로 떼어내니. 아래와 같은 깔끔한 Heap Graph 를 볼 수 있었다. 작은 용량으로 간단한 minor gc가 일어날 뿐이었다. minor gc는 5ms 이내이다.

 

 

 

jvm 메모리 관리를 하지 않아도 되니. 완전 마음 부담이 덜해지고, Full GC는 전혀 일어나지 않았다.

다만. cache 크기를 확인해 보는 것이 중요하긴 하지만, 캐쉬 용량 대비해서 전체 max memroy 크게 잡은지라. 큰 이슈는 없는 듯 하다.



주의할 점은 로컬 redis에 다른 서버들이 접근하지 않도록 redis.conf를 수정할 필요가 있다. 아래와 같이 수정하고 redis.conf를 읽도록 하면 웹 애플리케이션만 로컬 redis 를 접근할 수 있다.

bind 127.0.0.1


Posted by '김용환'
,

 

Redis를 단순 캐쉬로 사용하니  달랑 info와 monitor를 자주 사용하게 됨.

 

1) redis 정보 보기
role(master/slave여부), cpu, memory, cache hit 상태 확인

http://redis.io/commands/info

 

 

2) redis 컴맨드 확인
$ bin/redis-cli -p 포트번호
monitor (엔터)

http://redis.io/commands/info

 

그 외 괜찮은 명령어..

image

Posted by '김용환'
,

 

Apache Http 4.2 의 PoolingClientConnectionManager는 4.1의 ThreadSafeClientConnManager 의 구현방식을 새롭게 디자인해서 개발되었는데. 상당히 안정적이다..

4.1 이하의 ThreadSafeClientConnManager 사용할 때, 아주 가끔씩 원인을 알 수 없이 서버 쪽 thred pool이 차는 문제가 있었다.

 

기존의 API는 그대로 사용한 것이니. Apache Http 4.2 의 PoolingClientConnectionManager를 그냥 쓰면 될 듯.

 

http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/PoolingClientConnectionManager.html

Posted by '김용환'
,

 

현상)

shell script를 그냥 subversion에 그냥 commit 하면,

shell의 file type이 executable이 되지 않거나, ^M 이 줄 마지막에 들어가게 되어   /bin/bash^M: bad interpreter: No such file or directory 에러가 날 수 있다.

 

해결)

아래 property값이 commit되게 한다.

 

svn:eol-stype=native

svn:executable=*

 

 

image

Posted by '김용환'
,

 

tomcat 로그가 적으면 apache log rotate를 써도 무방하나, 그 이상일 때는 시스템 부하로 이어진다. 파일을 바꾸고 write 과정에서 io 가 burst 하게 일어나는 듯 하다. 


log4j의 DailyRollingFileAppender를 이용해서 로그 파일을 잘 분 부하를 최대한 줄여야 한다.

 

log4j 설정


    <appender name="general" class="org.apache.log4j.DailyRollingFileAppender">
        <param name="Threshold" value="DEBUG" />
        <param name="File" value="${catalina.base}/logs/${로그파일이름}" />
        <param name="DatePattern" value="'.'yyyyMMdd" />
        <param name="Append" value="true" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} [%-5p] (%F:%L) [%t] %m%n" />
        </layout>
    </appender>


    <root>
        <level value="WARN" />
        <appender-ref ref="general" />
    </root>

Posted by '김용환'
,

 

예전에 nginx 을 사용하던 한 웹 서비스가 장애가 나서 확인해 보니 can't identify protocol 라는 내용으로 소켓의 구분이 안되는 경우가 있었다.

 

$ lsof

can't identify protocol

 

ulimit –c 으로 지정된 파일 size를 넘어서면 문제가 되 었다.

 

socket leak (memory leak) 인데, nginx 버전을 최신으로 패치하면서 문제가 해결되었다. nginx는 async 서버라서 소켓을 제대로 close 못하면 이런 현상이 발견되었었다.

 

자세한 내용은 역시 아래 내용으로 살펴봐야 한다.

http://nginx.org/en/CHANGES

 

Bugfix: socket leak.
Posted by '김용환'
,

 

 

Spring 의 AbstractRoutingDataSource와 Mybatis의 Mapper를 이용하여 코딩을 했다는 내용의 글을 올렸다.

 

http://knight76.tistory.com/entry/mysql-%EC%97%90%EC%84%9C-%EB%8F%99%EC%9D%BC%ED%95%9C-table-schema%EB%A5%BC-%EA%B0%80%EC%A7%84-Multi-DB-%EB%A5%BC-java%EB%A1%9C-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0

 

 

Mybatis의 Mapper 클래스와 XML를 이용한 것은 기존의 Mybatis의 XML을 쓰기 편한 장점이 있었다.

 

 

대충 관련코드를 공유..

 

 

public class DynamicRoutingDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicContextHolder.getDataSourceName();
    }

}

 

 

 

mybatis의 SqlSessionFactoryBean를 상속받아 SqlSessionFactory를 생성하게 하는 Bean.

 

 

public class DynamicSqlSessionFactoryBean extends SqlSessionFactoryBean {

    private DataSource dataSource;

    private Resource configLocation;

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public void setConfigLocation(Resource configLocation) {
        this.configLocation = configLocation;
    }

    public SqlSessionFactory buildSqlSessionFactory() {
        super.setConfigLocation(configLocation);
        super.setDataSource(dataSource);
        SqlSessionFactory factory = null;
        try {
            factory = super.buildSqlSessionFactory();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return factory;
    }

    public void afterPropertiesSet() throws Exception {
    }

}

 

 

@Aspect
@Component
public class ExecutionAspect implements InitializingBean {

 

public Object distribute(ProceedingJoinPoint joinPoint) throws Throwable {
      

    … 중략..

 

 

        SqlSessionFactory s = createSqlSessionFactory(dataSourceNumber);


        SqlSession sqlSession = s.openSession();
        DynamicContextHolder.setSqlSession(sqlSession);

        Object returnValue = joinPoint.proceed();

        DynamicContextHolder.clear();
        return returnValue;

 

}

 

createSqlSessionFactory () 메소드가 DynamicSqlSessionFactoryBean 의 buildSqlSessionFactory를 매번 생성하도록 하였다.

 

처음에는 괜찮아보였지만. 성능상 큰 이슈가 발생했다.

 

 

mybatis의 mapper xml를 사용하기 위해서 매번 mybatis의 SqlSessionFactory생성하는 코드를 넣을 때 유의해야 한다. 성능이 떨어질 수 밖에 없다.

(또한, AbstractRoutingDataSource 사용 자체가 매번 DB connection을 DBPool로부터 fetch 해서 사용하고 close 하는 것도 성능 이슈이긴 했다. )

 

 

성능이 잘 안 나와서 덤프를 떠보니.. Blocked 된 쓰레드를 많이 발견했다.

XML을 매번 읽어서 SqlSessionFactory를 만드는 작업은 xerces lock 에 의해서 성능을 떨어뜨리도록 되어 있다.

 

 


"TP-Processor1275" daemon prio=10 tid=0x00002aaabc31c800 nid=0x11d4 waiting for monitor entry [0x0000000044f6f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.sun.org.apache.xerces.internal.impl.dv.DTDDVFactory.getInstance(DTDDVFactory.java:44)
    - waiting to lock <0x00000006f0854ae8> (a java.lang.Class for com.sun.org.apache.xerces.internal.impl.dv.DTDDVFactory)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.<init>(XML11Configuration.java:538)
    at com.sun.org.apache.xerces.internal.parsers.XIncludeAwareParserConfiguration.<init>(XIncludeAwareParserConfiguration.java:125)
    at com.sun.org.apache.xerces.internal.parsers.XIncludeAwareParserConfiguration.<init>(XIncludeAwareParserConfiguration.java:86)
    at sun.reflect.GeneratedConstructorAccessor10.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at java.lang.Class.newInstance0(Class.java:355)
    at java.lang.Class.newInstance(Class.java:308)
    at com.sun.org.apache.xerces.internal.parsers.ObjectFactory.newInstance(ObjectFactory.java:349)
    at com.sun.org.apache.xerces.internal.parsers.ObjectFactory.createObject(ObjectFactory.java:154)
    at com.sun.org.apache.xerces.internal.parsers.ObjectFactory.createObject(ObjectFactory.java:97)
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.<init>(DOMParser.java:133)
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.<init>(DOMParser.java:117)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.<init>(DocumentBuilderImpl.java:115)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl.newDocumentBuilder(DocumentBuilderFactoryImpl.java:72)
    at org.apache.ibatis.parsing.XPathParser.createDocument(XPathParser.java:237)
    at org.apache.ibatis.parsing.XPathParser.<init>(XPathParser.java:122)
    at org.apache.ibatis.builder.xml.XMLMapperBuilder.<init>(XMLMapperBuilder.java:74)
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XMLConfigBuilder.java:310)
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XMLConfigBuilder.java:104)
    at org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(XMLConfigBuilder.java:89)
    at org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:374)
    at jp.google.center.multidatasource.DynamicSqlSessionFactoryBean.buildSqlSessionFactory(DynamicSqlSessionFactory
Bean.java:42)
    at jp.google.center.multidatasource.ExecutionAspect.distribute(ExecutionAspect.java:110)

 

 

 

구글에 검색해보니.. 이미 성능 이슈가 난 것으로 알려져 있다. (fixed가 되어 있다.jaxb도 연관되어 있었음을 확인할 수 있다. )

https://issues.apache.org/jira/browse/CXF-3180

 

 

결국. AbstactDataSourceRouter를 제외하는 구조로 변경했다. 미리 Connection Pool과 Mybatis SqlSessionFactory를 초기화과정에서 만들어지도록 했다. 만들어진 SqlSessionFactory를 사용하도록 하니.

성능이 3배 정도로 나왔다.

'Web service' 카테고리의 다른 글

Tomcat log roate 관련  (0) 2012.08.30
nginx - can't identify protocol 이슈  (0) 2012.08.06
Spring MVC Async 예제 (example)  (1) 2012.06.23
WAS(Tomcat)의 보안성 높이기  (0) 2012.04.27
웹서버 성능과 유지보수  (0) 2012.03.29
Posted by '김용환'
,

 

참고자료.
http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/scheduling.html

 

 

beans-task.xml 생성. executer 정의

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:security="http://www.springframework.org/schema/security"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:task="http://www.springframework.org/schema/task"
    xsi:schemaLocation="
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
             http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd">
           
    <bean id="myexecuter" class="java.util.concurrent.Executors" factory-method="newFixedThreadPool">
        <constructor-arg value="100" />
    </bean>

    <task:executor id="threadPoolTaskExecutor" pool-size="100" queue-capacity="500"/>

    <task:annotation-driven executor="myexecuter" />
   
</beans>

 

 

 

beans.xml 에 추가 . ( 위치 중요)

    <import resource="classpath:/beans-task.xml" />

 

 

 

@Async 할 클래스 추가 (기존 Bean이 아닌 새로운 Bean으로 정의하니 동작함.)

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

 

@Service
public class AsyncProcessService {
      @Async
    public void processNotifications(List list) {

       // 작업
    }

}

 

 

 

Controller에 추가.

@Controller
public class EventController {

 

     @AutoWired

     AsyncProcessService asyncService;

 

    @RequestMapping(value = "/event", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody()
     public void test() {

         asyncService.processNotifications(newSendEventList);
         return Response.OK();

     }

}

Posted by '김용환'
,