powerdee.com
Google
 
このサイト内 Web
 
カウンタ

java.net.BindException: Address already in use: connect

インデックスへ戻る

データベースに接続できなくなる!

Spring + Hibernate + MySQL 環境で連続で更新クエリを実行すると以下のような例外がスローされる。

2006-10-20 23:43:59,468 [http-8080-Processor25] INFO org.springframework.beans.factory.xml.XmlBeanDef
initionReader - Loading XML bean definitions from class path resource [org/springframework/jdbc/suppo
rt/sql-error-codes.xml]
2006-10-20 23:43:59,500 [http-8080-Processor25] INFO org.springframework.jdbc.support.SQLErrorCodesFa
ctory - SQLErrorCodes loaded: [DB2, HSQL, MS-SQL, MySQL, Oracle, Informix, PostgreSQL, Sybase]
2006-10-20 23:44:58,203 [http-8080-Processor25] WARN org.hibernate.util.JDBCExceptionReporter - SQL E
rror: 0, SQLState: 08S01
2006-10-20 23:44:58,203 [http-8080-Processor25] ERROR org.hibernate.util.JDBCExceptionReporter - Comm
unications link failure due to underlying exception: 

** BEGIN NESTED EXCEPTION ** 

java.net.SocketException
MESSAGE: java.net.BindException: Address already in use: connect

STACKTRACE:

java.net.SocketException: java.net.BindException: Address already in use: connect
	at com.mysql.jdbc.StandardSocketFactory.connect(StandardSocketFactory.java:156)

改善前は、単一のエンティティを受け取ってgetHibernateTemplate().saveを呼ぶだけ。これを12万回繰り返していた。

    /**
     * 登録処理
     * 
     * @param entity
     */
    public void save(Address entity) {
        getHibernateTemplate().save(entity);
    }

理由は、MySQLへのTCPコネクションが、更新クエリ発行毎に張られてしまいポート番号が実行環境(WindowsXP)で使い切ってしまった為。
ポート番号は、65534まであるが、Windowsのデフォルトは5000までとなっているらしい。
また、アプリから更新クエリ終了後close処理をしても、TCPコネクションはTIME_WAITの状態をキープする。

※参考 http://www.mysql.gr.jp/mysqlml/mysql/msg/9648 より一部抜粋。

Windows2000(そしてXPもだと思いますが)には、ポートが足りなくなった場合に、TIME_WAITに遷移してから60秒
以上経過した接続を強制的にCLOSEDの状態へ遷移させる(つまりポートを空ける)緊急クリーンアップの機能を持
っていますし、短時間に大量の接続/切断を繰り返すのはプログラミング的にアウチですから、通常はあまりポ
ートが足りなくなってエラーが発生する事はありません。
けれども、MSのTCP/IP関係のレジストリ設定を変更するして(例えば、HKEY_LOCAL_MACHINE\SYSTEM\CurrentCont
rolSet\Services\Tcpip\Parametersで、MaxFreeTWTCBsを6000、MaxUserPortを5000とするとか)、短時間に大量
の接続/切断を繰り返すと、ポートが足りなくてエラーが出る状態を作りだす事が出来ます。

※もろにこの状態を作り出してしまっているじゃん^^;

対応方法

エンティティのListを受け取ってJDBCのexecuteBatch()メソッドで実行するようにした。 ソースを以下のように改善した。

    /**
     * 登録処理。
     * 
     * @param List<Address>
     */
    public void save(List<Address> addressList) throws SystemException {
        Connection con = null;
        PreparedStatement stmt = null;
        try {
            String insertSql 
                = "INSERT INTO ADDRESS VALUES(null,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
            con = getJdbcTemplate().getDataSource().getConnection();
            stmt = con.prepareStatement(insertSql);
            
            for (Iterator<Address> ite = addressList.iterator(); ite.hasNext();) {
                Address address = ite.next();
                
                stmt.setInt(1, address.getLocalAuthorityCd());
                stmt.setString(2, address.getOldZipcode());
                stmt.setString(3, address.getZipcode());
                stmt.setString(4, address.getPrefKana());
                stmt.setString(5, address.getCityKana());
                stmt.setString(6, address.getAddrKana());
                stmt.setString(7, address.getPref());
                stmt.setString(8, address.getCity());
                stmt.setString(9, address.getAddr());
                stmt.setString(10, address.getFlg1());
                stmt.setString(11, address.getFlg2());
                stmt.setString(12, address.getFlg3());
                stmt.setString(13, address.getUpdateFlg());
                stmt.setString(14, address.getUpdateReason());
                
                stmt.addBatch();
            }
            
            int result[] = stmt.executeBatch();
            
            logger.debug(result.length + " 行インサートされました。");
            
            stmt.close();
            con.close();
        } catch (SQLException e) {
            throw new SystemException(e);
        } finally {
            try {
                if (stmt!=null) stmt.close();
                if (con!=null) con.close();
            } catch (Exception e) {
                throw new SystemException("DB切断に失敗", e);
            }
        }
    }

これだと、TCPコネクションが1個だけになって、全く問題無い。
netstat -an で確認しました。

    TCP    127.0.0.1:3306         127.0.0.1:1626         ESTABLISHED

インデックスへ戻る


ページTopへ / ▲Homeへ