dbcp 분석

12943 단어 db자바
1. 목적
DBCP 는 주로 jdbc 에 연결 풀 서 비 스 를 제공 합 니 다.
 
2. 실현
2.1 Jakarta Commons Pool
DBCP 는 연결 풀 관 리 를 위해 자카르타 커 먼 즈 풀 을 활용 했다.Commons Pool 의 기본 개념 을 살 펴 보 겠 습 니 다.
Poolable Object Factory: 풀 화 된 대상 의 생 성, 활성화, 걸 기, 검사 와 소각 을 관리 하 는 데 사 용 됩 니 다.ObjectPool:  풀 화 될 대상 의 대출 과 반환 을 관리 하고 Poolable Object Factory 에 해당 하 는 작업 을 완성 하 라 고 통지 합 니 다.
 
사용 과정:
1. 사용 할 Poolable Object Factory 클래스 의 인 스 턴 스 생 성
PoolableObjectFactory factory = new PoolableObjectFactorySample();


2. 이 Poolable ObjectFactory 인 스 턴 스 를 매개 변수 로 ObjectPool 인 터 페 이 스 를 실현 하 는 클래스 (예 를 들 어 Stack ObjectPool) 의 인 스 턴 스 를 생 성하 여 대상 풀 로 합 니 다.
ObjectPool pool = new StackObjectPool(factory); 

3. 대상 풀 에서 대상 을 꺼 내야 할 때 대상 풀 의 Object borrowObject () 방법 을 호출 합 니 다.
obj = pool.borrowObject(); 


4. 대상 을 대상 풀 에 다시 넣 어야 할 때 이 대상 풀 의 void returnObject (Object obj) 방법 을 호출 합 니 다.
pool.returnObject(obj); 
5.              ,       void close()  ,         。 
pool.close(); 

Poolable Object Factory 의 예 를 들 어 보 겠 습 니 다.
public class PoolableObjectFactorySample implements PoolableObjectFactory {

   public Object makeObject() throws Exception {
        return new Object();
    }

    public void activateObject(Object obj) throws Exception {
        System.err.println("Activating Object " + obj);
    }

    public void passivateObject(Object obj) throws Exception {
        System.err.println("Passivating Object " + obj);
    }

    public boolean validateObject(Object obj) {
        return true;
    }
    public void destroyObject(Object obj) throws Exception {
        System.err.println("Destroying Object " + obj);
    }
}

이상 의 내용 을 통 해 comon pool 의 기본 용법 을 알 게 되 었 을 것 이 라 고 믿 습 니 다.
 
2.2 DBCP
일반적인 jdbc 연결 은 대개 다음 과 같 습 니 다.
public static Connection getConn() throws Exception { 
 Class.forName("oracle.jdbc.driver.OracleDriver");  
        Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@10.20.143.12:1521:crmp?args[applicationEncoding=UTF-8,databaseEncoding=UTF-8]","eve", "ca");  
        return conn;
}


그리고 지 화 를 고려 하면 우리 가 생각 할 수 있 는 첫 번 째 단 계 는 getConn () 을 방금 Poolable Object Factory 의 MakeObject 에 넣 는 것 이다.
dbcp 는 사실 이 일보 다 조금 더 많이 했 습 니 다. 먼저 다음 그림 을 보 겠 습 니 다.
datasource 와 connection 은 모두 decorator 로 포장 되 었 습 니 다.이것 도 decorator 의 전형 적 인 용법 이다.
가장 중요 한 것 은 Poolable Connection 이 ObjectPool 의 인용 을 가지 고 있어 야 connection 이 사 용 된 후에 언제든지 풀 에 넣 을 수 있 고 conn 의 사용 자 는 ObjectPool 에 관심 을 가 질 필요 가 없다 는 것 이다.
주요 createDatasource 과정 은 다음 과 같 습 니 다.
  protected synchronized DataSource createDataSource()
        throws SQLException {
        if (closed) {
            throw new SQLException("Data source is closed");
        }

        // Return the pool if we have already created it
        if (dataSource != null) {
            return (dataSource);
        }

        // create factory which returns raw physical connections
        ConnectionFactory driverConnectionFactory = createConnectionFactory();

        // create a pool for our connections
        createConnectionPool();

        // Set up statement pool, if desired
        GenericKeyedObjectPoolFactory statementPoolFactory = null;
        if (isPoolPreparedStatements()) {
            statementPoolFactory = new GenericKeyedObjectPoolFactory(null,
                        -1, // unlimited maxActive (per key)
                        GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL,
                        0, // maxWait
                        1, // maxIdle (per key)
                        maxOpenPreparedStatements);
        }

        // Set up the poolable connection factory
        createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig);

        // Create and return the pooling data source to manage the connections
        createDataSourceInstance();
        
        try {
            for (int i = 0 ; i < initialSize ; i++) {
                connectionPool.addObject();
            }
        } catch (Exception e) {
            throw new SQLNestedException("Error preloading the connection pool", e);
        }
        
        return dataSource;
    }

연결 풀 (datasource) 을 만 드 는 과정 은 상기 코드 를 통 해 잘 알 수 있 습 니 다.
 
연결 이 실 효 될 때의 처 리 를 추가 합 니 다.
  • 데이터베이스 에서 tcp 연결 을 정상적으로 닫 은 상태 에서 (Oacle 의 kill process 와 같은 FIN 패 키 지 를 보 냈 습 니 다) connection 에서 감지 할 수 있 습 니 다.
  • 데이터베이스 에서 tcp 연결 을 정상적으로 닫 지 않 은 상태 에서 (session 이 유효 하지 않 습 니 다. 예 를 들 어 DB 서버 에서 연결 이 끊 겼 습 니 다. Oacle 에서 session 을 죽 였 습 니 다. my sql 에서 최대 시간 을 초과 하여 연결 이 끊 겼 습 니 다) 이러한 상황 에서 응용 프로그램의 연결 은 계속 사용 할 수 있 습 니 다. insert, commt 와 같은 조작 을 사용 해 야 이상 을 던 질 수 있 습 니 다
  • 연결 이 끊 어 진 이상 까지 catach 를 사용 할 때 conn 을 방출 합 니 다. 즉, conn. close 를 호출 합 니 다. 이 때 는 연결 이 잘못 되 었 다 면 연결 풀 을 다시 놓 지 않 습 니 다.따라서 데이터 베 이 스 를 전환 하면 이 이론 을 응용 하면 재 부팅 하지 않 아 도 자 를 수 있다 는 것 을 의미한다.
        public synchronized void close() throws SQLException {
            boolean isClosed = false;
            try {
                isClosed = isClosed();
            } catch (SQLException e) {
                try {
                    _pool.invalidateObject(this);
                } catch (Exception ie) {
                    // DO NOTHING the original exception will be rethrown
                }
                throw new SQLNestedException("Cannot close connection (isClosed check failed)", e);
            }
            if (isClosed) {
                throw new SQLException("Already closed.");
            } else {
                try {
                    _pool.returnObject(this);
                } catch(SQLException e) {
                    throw e;
                } catch(RuntimeException e) {
                    throw e;
                } catch(Exception e) {
                    throw new SQLNestedException("Cannot close connection (return to pool failed)", e);
                }
            }
        }

     
    2.3 연결 풀 상용 설정
     
    자주 사용 하 는 설정 을 먼저 붙 입 니 다:
     
            
            
            
            
            
            
            
            
            
            
            
            
            
    

    사실 대부분의 설정 은 common. pool 의 설정 입 니 다. 풀 이 라 고 하면 최대 활동, 최대 여가, 최소 여가, 여가 시간 등 개념 을 생각 합 니 다.여기 서 주로 설명: max Active, max Idle 두 매개 변수
     
  • max Active: 최대 활동 연결 수, 연결 탱크 는 이렇게 사용 합 니 다
  • if(_maxActive < 0 || _numActive < _maxActive) {
                        // allow new object to be created
                    } else {
                        // the pool is exhausted
                        switch(_whenExhaustedAction) {
                            case WHEN_EXHAUSTED_GROW:
                                // allow new object to be created
                                break;
       ....
                            default:
                                throw new IllegalArgumentException("WhenExhaustedAction property " + _whenExhaustedAction + " not recognized.");
                        }
                    }
                }
                _numActive++;
    

    여기 서 볼 수 있 습 니 다. 매번 대상 을 만 들 때마다numActive +, 만 든 대상 > = 최대 값 일 때 새 대상 은 줄 을 서서 기 다 려 야 합 니 다.기본 시간 (설정 중의 maxWait) 을 초과 하면 다시 던 집 니 다. throw new NoSuchElementException("Timeout waiting for idle object");우리 가 흔히 볼 수 있 는 연결 이 되 지 않 는 상황 이 발생 한다.
     
  • maxIdle: 풀 안의 최대 남 은 연결 수, 기본 설정 은 8
  • 입 니 다.
     if (decrementNumActive) {
                _numActive--;
            }
            if((_maxIdle >= 0) && (_pool.size() >= _maxIdle)) {
                shouldDestroy = true;
            } else if(success) {
                _pool.addLast(new ObjectTimestampPair(obj));
            }
            notifyAll(); // _numActive has changed
    
            if(shouldDestroy) {
                try {
                    _factory.destroyObject(obj);
                } catch(Exception e) {
                    // ignored
                }
            }
    
    

    return 과정 에서 우 리 는 볼 수 있 습 니 다. 만약pool.size() >= _maxIdle, 연결 이 직접 소 모 됩 니 다. dbcp 의 기본 설정 에서 이곳 의 값 은 8 이 고 maxative 는 28 입 니 다. 이러한 가장 큰 문 제 는 높 은 동시 다발 로 비 추 는 상황 에서 일부 연결 이 반환 할 때 소각 되 고 곧 동시 다발 로 올 라 올 때 생 성 될 수 있 습 니 다. 생 성 은 자원 을 소모 하 는 일 입 니 다.또한 Public synchronized Object borrowObject () 는 동기 화 방법 이기 때문에 높 은 동시 다발 시 대량의 스 레 드 block 이 나타 납 니 다. 다음은 20 동시 다발 조회 의 테스트 결과 입 니 다 (sql 자 체 를 조회 하 는 데 시간 이 많이 걸 리 지 않 습 니 다).
    위 는 maxive = 28, maxidle = 8 의 경우 응답 시간 과 tps 는 말 할 필요 가 없습니다.조정 후의 스 레 드 가 어느 정도 막 힌 상황 은 ibatis, ibatis 를 사 용 했 기 때 문 입 니 다. 상세 한 것 은 부록 [1] 을 참조 하 십시오.
  • timeBetweenEvictionRuns Millis: 한 번 에 쫓 기 는 시간 이 얼마 인지 표시 합 니 다.

  • 쫓 아 내 는 대략적인 원 리 는 백 스테이지 timer 가 시간 간격 으로 BetweenEvictionRuns Millis 에서 스 레 드 를 만 들 고 연못 의 return Math. min (numTestsPerEvictionRun, _pool.size());연결
     if ((_minEvictableIdleTimeMillis > 0) && (idleTimeMilis > _minEvictableIdleTimeMillis)) {
                        removeObject = true;
     }

    하면, 만약, 만약...minEvictable IdleTimeMillis (기본 값 은 30 분, 같은 설정 가능) 를 소각 합 니 다.test While Idle 이 설정 되 어 있 으 면 이 연결 이 사용 가능 한 지, 사용 할 수 없 는 지, 마찬가지 로 삭 제 됩 니 다.한 번 에 numTests PerEvictionRun 연결 만 처리 하고 한 번 에 다 처리 하지 않 습 니 다.
  • minIdle: 최소 남 은 수
  • 이 값 에 대해 어떤 것 은 0 으로 배합 하 는 것 을 추천 하고, 어떤 것 은 2 로 배합 하 는 것 을 추천 하 며, 각각 설 이 있다.
    0 으로 추천 하 는 이 유 는:
    public void run() {
                try {
                    evict();
                } catch(Exception e) {
                    // ignored
                }
                try {
                    ensureMinIdle();
                } catch(Exception e) {
                    // ignored
                }
            }
    

    evict () 는 대상 의 남 은 시간 을 검사 하고 사용 하지 않 는 대상 을 삭제 한 다음 ensureMinIdle () 은 풀 에 minIdle 개 이상 의 대상 이 있 는 지 확인 하고 없 으 면 대상 을 만 듭 니 다.이 는 응용 이 한가 할 때 모든 대상 을 삭제 한 다음 에 두 개의 대상 을 다시 만 들 고 나중에 삭제 하 는 디 더 링 상황 을 초래 할 수 있 습 니 다.현재 설정 은 30 분 마다 쫓 겨 나 는 것 으로 30 분 마다 한 번 더 소모 된다.
    2 로 배 치 된 장점 은 응용 이 비교적 한가 할 때 적어도 요청 할 때마다 연결 을 다시 만 들 지 않 는 다 는 것 이다.
     
    부록:
    [1] ibatis 로 인 한 스 레 드 차단 문제
    1.     위의 차단 에 대해 서 는 Dump 스 레 드 를 통 해 ibatis 가 결 과 를 처리 할 때 나타 난 것 을 발견 하 였 습 니 다.
     
    
    at com.ibatis.common.beans.ClassInfo.getInstance(ClassInfo.java:362)
    
    - locked <0x00002aaaae2c9b60> (a java.lang.Class for java.util.HashMap)
    
             at com.ibatis.common.beans.ComplexBeanProbe.setProperty(ComplexBeanProbe.java:328)
    
             ….
    
    

     
    ClassInfo. getInstance 에 대응 하 는 코드 는:
     
    if (cacheEnabled) {
    
    synchronized (clazz) {
    
    ClassInfo cache = (ClassInfo) CLASS_INFO_MAP.get(clazz);
    
    if (cache == null) {
    
    cache = new ClassInfo(clazz);
    
    CLASS_INFO_MAP.put(clazz, cache);
    
    }
    
    return cache;
    
    }
    
    

     
      위의 이 코드 의 목적 은 무엇 입 니까?ibatis 는 조회 결 과 를 얻 은 후 driver 가 되 돌아 오 는 object [] 를 resultMap 에서 지정 한 대상 (set * 방법 을 통 해) 에 반사 적 으로 설정 해 야 합 니 다.성능 을 고려 하여 (getMethod 를 반사 적 으로 호출 하 는 데 상대 적 으로 많은 시간 이 걸 립 니 다) ibatis 는 class 를 key 로 사용 하여 사용 하 는 set 방법 을 map 에 캐 시 하여 반사 적 으로 호출 할 수 있 습 니 다.여러 번 초기 화 되 지 않도록 동기 화 를 사용 했다.그러나 높 은 병발 상황 에서 (그리고 우리 의 테스트 는 같은 대상 의 데 이 터 를 얻 는 것) 이 동기 화 는 차단 을 초래 합 니 다. 즉, 테스트 보고서 에 스 레 드 block 을 새로 설정 한 곳 입 니 다. 
       위의 모든 문 제 를 설명 한 후에 또 하나의 의문 이 있 습 니 다. 첫 번 째 호출 을 제외 하고 매번 이 함 수 를 호출 할 때마다 조작 만 합 니 다.
    ClassInfo cache = (ClassInfo) CLASS_INFO_MAP.get(clazz)
    
    

    이 방법 은 비록 동기 화 되 었 지만, 실행 은 매우 빠 른 데, 왜 여러 개의 라인 이 막 힐 수 있 습 니까?
    조사 연구 에 따 르 면 이 방법 은 모든 속성 을 설정 할 때 한 번 호출 되 기 때 문 입 니 다. 하나의 조회 가 20 개의 기록 을 되 돌려 준다 고 가정 하고 하나의 기록 이 10 개의 속성 이 있다 고 가정 하면 하나의 결과 조합 은 적어도 200 번 호출 됩 니 다. 자 물 쇠 를 풀 때 메모리 구 조 를 수정 할 뿐 스 레 드 를 전환 하지 않 습 니 다 (자바 에서 block 의 스 레 드 는 Liux 에서 sleep 상태 이 고 대기 열 에 있 습 니 다).따라서 일반적인 상황 에서 현재 자 물 쇠 를 가 진 스 레 드 는 이 자 물 쇠 를 반복 적 으로 가지 고 200 번 연속 으로 호출 한 다음 에 다른 스 레 드 가 다시 경쟁 할 때 한 스 레 드 만 자물쇠 에 경쟁 합 니 다. 그러면 운 이 가장 나 쁜 스 레 드 는 (19 * 200) 번 의 조작 을 기다 릴 것 입 니 다. 매번 시간 이 짧 지만 합 쳐 도 일정한 차단 상황 이 있 습 니 다.
     
     
    참고 문헌: 자카르타 Commons Pool 처리 대상 지 화 사용:http://www.ibm.com/developerworks/cn/java/l-common-pool/index.html
     
     
     

    좋은 웹페이지 즐겨찾기