جافا ConnectionPool اتصال لا تغلق ، عالقة في 'النوم'

StackOverflow https://stackoverflow.com/questions/53128

  •  09-06-2019
  •  | 
  •  

سؤال

لدي webapp يستخدم JNDI عمليات البحث للحصول على اتصال قاعدة البيانات.

الاتصال يعمل بشكل جيد و إرجاع الاستعلام أية مشاكل.المشكلة أن الاتصال لا تغلق بشكل صحيح و هي عالقة في 'النوم' وضع (وفقا mysql administrator).وهذا يعني أنها تصبح غير صالحة للاستعمال nad ثم نفد من اتصالات.

شخص ما يمكن أن تعطيني بعض المؤشرات على ما يمكنني القيام به لجعل الاتصال العودة إلى تجمع بنجاح.

public class DatabaseBean {

private static final Logger logger = Logger.getLogger(DatabaseBean.class);

private Connection conn;
private PreparedStatement prepStmt;

/**
 * Zero argument constructor
 * Setup generic databse connection in here to avoid redundancy
 * The connection details are in /META-INF/context.xml
 */
public DatabaseBean() {
    try {
        InitialContext initContext = new InitialContext();
        DataSource ds = (DataSource) initContext.lookup("java:/comp/env/jdbc/mysite");
        conn = ds.getConnection();
    }
    catch (SQLException SQLEx) {
        logger.fatal("There was a problem with the database connection.");
        logger.fatal(SQLEx);
        logger.fatal(SQLEx.getCause());
    }
    catch (NamingException nameEx) {
        logger.fatal("There was a naming exception");
        logger.fatal(nameEx);
        logger.fatal(nameEx.getCause());
    }
}

/**
 * Execute a query. Do not use for statements (update delete insert etc).
 *
 * @return A ResultSet of the execute query. A set of size zero if no results were returned. It is never null.
 * @see #executeUpdate() for running update, insert delete etc.
 */

public ResultSet executeQuery() {
    ResultSet result = null;
    try {
        result = prepStmt.executeQuery();
        logger.debug(prepStmt.toString());
    }
    catch (SQLException SQLEx) {
        logger.fatal("There was an error running a query");
        logger.fatal(SQLEx);
    }
    return result;
}

قص

public void close() {
    try {
        prepStmt.close();
        prepStmt = null;

        conn.close();
        conn = null;
    } catch (SQLException SQLEx) {
        logger.warn("There was an error closing the database connection.");
    }
}
}

هذا هو داخل التخطيط الاستراتيجي المشترك javabean التي تستخدم اتصال قاعدة البيانات.

public LinkedList<ImportantNoticeBean> getImportantNotices() {

    DatabaseBean noticesDBBean = new DatabaseBean();
    LinkedList<ImportantNoticeBean> listOfNotices = new LinkedList<ImportantNoticeBean>();

    try {
        PreparedStatement preStmt = noticesDBBean.getConn().prepareStatement("SELECT pseudonym, message, date_to, date_from " +
                "FROM importantnotices, users " +
                "WHERE importantnotices.username = users.username " +
                "AND NOW() >= date_from AND NOW() <= date_to;");

        noticesDBBean.setPrepStmt(preStmt);
        ResultSet result = noticesDBBean.executeQuery();

        while (result.next()) {
            ImportantNoticeBean noticeBean = new ImportantNoticeBean();

            noticeBean.setAuthor(result.getString("pseudonym"));
            noticeBean.setMessage(result.getString("message"));
            noticeBean.setDateTo(result.getDate("date_to"));
            noticeBean.setDateFrom(result.getDate("date_from"));

            listOfNotices.add(noticeBean);
        }

        result.close();

    } catch (SQLException SQLEx) {
        logger.error("There was an error in ImportantNoticesBean.getImportantNotices()");
        logger.error(SQLEx);
    } finally {
        noticesDBBean.close();
    }
    return listOfNotices;
}

<Context reloadable="true">

    <Resource name="jdbc/mysite"
              auth="Container"
              type="javax.sql.DataSource"
              username="user"
              password="password"
              driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://localhost:3306/mysite"
              maxActive="10"
              maxIdle="5"
              maxWait="6000"
              removeAbandoned="true"
              logAbandoned="false"
              removeAbandonedTimeout="20"
            />
</Context>
هل كانت مفيدة؟

المحلول 4

المشكلة أن الاتصال لا تغلق بشكل صحيح و هي عالقة في 'النوم' واسطة

كان هذا في الواقع سوى نصف الحقيقة.

المشكلة صادفت في الواقع أن كل التطبيق تم تحديد اتصال جديد إلى قاعدة البيانات سيفر.لذلك في كل مرة أغلقت جميع اتصالات التطبيق من شأنه أن يجعل مجموعة من الاتصالات في الأمر WEB.xml config وتشغيل بسعادة.التطبيق ب أن تفعل الشيء نفسه.المشكلة هي أنها المستقلة برك التي تحاول الاستيلاء على ما يصل إلى الملقم تحديد الحد.بل هو نوع من حالة السباق أعتقد.حتى عندما يكون التطبيق قد انتهى مع اتصالات يجلس في انتظار استخدامها مرة أخرى حتى مهلة قد مرت في حين أن التطبيق ب الذي يحتاج الاتصال الآن محرومة من الموارد على الرغم من التطبيق قد انتهى ويجب أن يكون مرة أخرى في حوض السباحة.مرة واحدة المهلة قد مرت ، اتصال حررت و ب (أو ج الخ) يمكن أن تحصل في ذلك مرة أخرى.

على سبيل المثالإذا كان الحد الأقصى هو 10 (الخلية تعريف الحد) و كل التطبيق قد تم تكوينه لاستخدام أقصى 10 سيكون هناك 20 محاولات اتصالات.من الواضح أن هذا الوضع السيئ.

الحل هو RTFM ووضع تفاصيل الاتصال في المكان المناسب.هذا لا تجعل مشترك نشر الألم ولكن هناك طرق حوله (مثل ربط الأخرى ملفات xml من السياق).

لمجرد أن يكون واضحا:أضع تفاصيل الاتصال في WEB.xml لكل التطبيق و تشاجرنا حول هذا الموضوع.

نصائح أخرى

يبدو أن إغلاق الاتصال بشكل صحيح - باستثناء الحالة التي يكون فيها prepStmt.close() يلقي SQLException, لا أستطيع العثور على تسرب اتصال.

ما تجمع تنفيذ تستخدمه ؟ عند إغلاق اتصال تجمع ضرورة عدم إغلاق الأساسية اتصال الخلية مباشرة - بعد كل هذا هو نقطة اتصال بركة!لذا من الخلية جانب الاتصالات تبدو على قيد الحياة ، على الرغم من أن التطبيق الخاص بك هو عدم استخدام أي ؛ قد يكون ببساطة عقد TC تجمع الاتصال.

قد ترغب في تجربة مع إعدادات تجمع الاتصال.أسأل أن يتقلص المسبح عندما يكون النظام خاملا.أو يطلب منه تحديث كافة الاتصالات بشكل دوري.أو صارمة العلوي على عدد الاتصالات المتزامنة من أي وقت مضى يحصل من الخلية.... الخ

طريقة واحدة للتحقق إذا كان لديك رمز اتصال تسرب إلى قوة ds.getConnection() دائما فتح اتصال المادية و كون.close() الإفراج عن الاتصال (إذا كان اتصالك تجمع إعدادات أولئك).ثم إذا كان يمكنك مشاهدة الروابط على الخلية الجانب, كنت قد تكون قادرة على معرفة ما إذا كان رمز حقا لديه اتصال تسرب أم لا.

هذا هو سؤال مماثل - تجمع الاتصال إعدادات هر

هذا هو ردي على هذا السؤال و إصلاح المشكلة على الرجل الآخر.قد يساعدك أيضا.

هر الوثائق

DBCP يستخدم جاكرتا-المشاع قاعدة بيانات تجمع الاتصال.لأنه يعتمد على عدد من جاكرتا-المشاع المكونات:

* Jakarta-Commons DBCP
* Jakarta-Commons Collections
* Jakarta-Commons Pool

أنا باستخدام نفس اتصال تجميع الاشياء وانا تحديد هذه الخصائص لمنع الشيء نفسه انه فقط لم يتم تكوينه من خلال هر.ولكن إذا كان أول شيء لا يعمل جرب هذه.

testWhileIdle=true
timeBetweenEvictionRunsMillis=300000

حسنا ربما يكون هذا فرزها.لقد تغيرت قاعدة بيانات التكوين الموارد التالية:

*SNIP*
maxActive="10"
maxIdle="5"
maxWait="7000"
removeAbandoned="true"
logAbandoned="false"
removeAbandonedTimeout="3"
*SNIP*

هذا يعمل جيدا بما فيه الكفاية الآن.ما يحدث ، afaik ، هو أنه بمجرد أن تصل إلى عشرة اتصالات ثم هر هو التحقق من التخلي عن اتصالات (وقت الخمول > 3).يفعل هذا في مهمة دفعي في كل مرة أن ماكس الاتصالات التي تم التوصل إليها.مشكلة محتملة مع هذا إذا كنت تحتاج إلى أكثر من 10 الاستفسارات تشغيل في نفس الوقت (ليست فريدة من نوعها بالنسبة لي).الشيء المهم هو أن removeAbandonedTimeout أقل من maxWait.

هذا ما يجب أن يحدث ؟ أي هو هذه الطريقة التي تجمع يجب أن تعمل ؟ إذا كان يبدو ، على الأقل بالنسبة لي, أن تنتظر حتى شيء (الاتصال) مكسورة قبل تحديد بدلا من عدم السماح لها 'كسر' في المقام الأول.ربما أنا لا تزال لا تحصل على ذلك.

شيء واحد @binil غاب ، أنت لا تغلق مجموعة النتائج في حالة استثناء.اعتمادا على تطبيق برنامج التشغيل قد يتسبب هذا الصدد أن تبقى مفتوحة.الانتقال من النتيجة.close() الدعوة إلى كتلة أخيرا.

أنا باستخدام نفس التكوين كما أنت.إذا كان الاتصال في الخلية المسؤول(ويندوز) يدل على أنه في وضع السكون فهو يعني فقط أن يتم تجميع ولكن ليس في الاستخدام.راجعت هذا تشغيل اختبار برنامج البرنامج مع العديد من المواضيع بشكل عشوائي الاستفسارات إلى الخلية.إذا كان ذلك يساعد هنا هو بلدي التكوين:

        defaultAutoCommit="false"
        defaultTransactionIsolation="REPEATABLE_READ"
        auth="Container"
        type="javax.sql.DataSource"
        logAbandoned="true" 
          removeAbandoned="true"
        removeAbandonedTimeout="300" 
        maxActive="-1"
        initialSize="15"
        maxIdle="10"
        maxWait="10000" 
        username="youruser"
        password="youruserpassword"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://yourhost/yourdatabase"/>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top