Lỗi đóng kết nối sql qua asp.net

-Khi lập trình với Connection Pool, vấn đề muôn thuở mà bất kì lập trình viên có kinh nghiệm hay không đó là để lọt khe connection, dẫn đến tràn pool và khách hàng của bạn hay nhận được các màn hình vàng khè chữ đỏ tiếng Anh báo lỗi. Cho dù bạn đã cố gắng gọi lệnh đóng connection.Close[] mỗi khi sử dụng hay kể cả try catch trong đoạn code của mình. Bài viết này hy vọng giúp bạn có một cái nhìn và giải quyết vấn đề đóng kết nối và sử dụng hiệu quả connection pool cho ứng dụng của bạn.

Việc mở một kết nối tới cơ sở dữ liệu là một quá trình hoạt động cần nhiều thời gian và tiêu tốn tài nguyên. Việc tạo vùng chung cho các kết nối làm tăng hiệu suất cho các ứng dụng web bằng cách tái sử dụng các kết nối đang hoạt động thay vì tạo thêm kết nối mới mỗi khi có yêu cầu. Trình quản lý vùng kết nối chung [Connection Pool manager – CPM] duy trì và vận hành một vùng các kết nối cơ sở dữ liệu ở trạng thái mở. Mỗi khi có một yêu cầu kết nối mới, CPM sẽ kiểm tra Pool xem có kết nối nào đang rảnh sẽ trả về trạng thái sẵn sàng phục vụ. Nếu tất cả các kết nối hiện tại trong Pool đều bận và kích thước Pool chưa đạt cực đại, thì một kết nối mới sẽ được tạo và đưa thêm vào Pool. Khi kích thước Pool đạt tới cực đại, các yêu cầu kết nối mới sẽ được đặt vào hàng đợi cho đến khi một kết nối trong Pool trở về trạng thái sẵn sàng hoặc có một kết nối hết TimeOut.

Các hoạt động tạo vùng chung kết nối được kiểm soát bằng các tham số trong chuỗi kết nối [connection string]. Có 4 tham số điển hình là:

Connect Timeout – kiểm soát thời gian chờ tính bằng giây khi có yêu cầu kết nối mới, nếu quá khoảng thời gian này, một thông báo ngoại lệ sẽ được bắn ra [exception]. Giá trị ngầm định là 15 giây.

Max Pool Size – định ra kích thước cực đại của Pool, ngầm định là 100 kết nối, hầu hết các web site dùng không quá 40 kết nối dưới mức tải nặng nhất, nhưng nó lại phụ thuộc vào thao tác cơ sở dữ liệu của bạn mất bao lâu để hoàn thành.

Min Pool Size – số kết nối khởi tạo được dưa vào Pool khi bắt đầu, ngầm định là 0, tuy nhiên bạn có thể đặt ra một số cụ thể, ví dụ là 5, nếu ứng dụng của bạn cần thời gian phản hồi lớn để đảm bảo tình kiên định kết nối, mặc dù sau đó có thể nó chẳng được dùng trong hàng giờ. Trong trường hợp này, yêu cầu kết nối lần đầu sẽ không phải chờ khi thiết lập kết nối.

Pooling – tham số này báo hiệu bạn có dùng Pool hay không. Ngầm định là True, tuy nhiên bạn cần đọc ra giá trị của nó và gán giá trị cho nó để chắc chắn nó không phải False nếu bạn muốn bật/ tắt chế độ Pool.

Các giải pháp cho các vấn đề thường gặp

Các vấn đề khi tạo vùng kết nối chung thường gây ra bởi lỗ rò kết nối [“connection leak”] – một điều kiện khi ứng dụng của bạn không đóng kết nối tới cơ sở dữ liệu một cách đúng đắn và kiên định [correctly and consistently]. Khi bạn để lọt khe [“leak”] các kết nối, chúng vẫn mở cho đến khi bộ thu thập rác[garbage collector – GC] đóng chúng lại bằng cách gọi phương thức Dispose[]. Không giống như các bộ ADO cũ, ADO .NET yêu cầu bạn phải đóng thủ công các kết nối CSDL của mình mỗi khi bạn hoàn thành thao tác với chúng. Nếu bạn quá tin cậy vào việc thu dọn tự động của hệ thống, hãy nghĩ lại vì các kết nối này có thể tồn tại hàng giờ trước khi GC tự động đóng chúng lại. Điều này có nghĩa là ứng dụng của bạn có thể đã bị chết ngập rất lâu rồi, khi này khách hàng của bạn thường nhìn thấy một màn hình vàn và đỏ các dòng thông báo dạng như sau:

Exception: System.InvalidOperationException Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached. Source: System.Data at System.Data.SqlClient.SqlConnectionPoolManager.Get PooledConnection[SqlConnectionString options, Boolean& isInTransaction] at System.Data.SqlClient.SqlConnection.Open[] …

Exception: System.InvalidOperationException

Message: Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

Source: System.Data

at System.Data.SqlClient.SqlConnectionPoolManager.Get PooledConnection[SqlConnectionString options, Boolean& isInTransaction]

at System.Data.SqlClient.SqlConnection.Open[]

Đóng kết nối

Khi bạn có ý định đóng các kết nối tới CSDL của mình, bạn cần chắc chắn rằng bạn đã thưc sự ngắt được kết nối đó. Đoạn code sau sẽ minh họa cho một kết nối đã bị bỏ qua:

Mã:

SqlConnection conn = new SqlConnection[myConnectionString]; conn.Open[]; doSomething[]; conn.Close[];

Nếu lệnh doSomething[] ném ra một ngoại lệ [exception] – conn sẽ không bao giờ được đóng cả. Bạn cần code đúng như sau:

SqlConnection conn = new SqlConnection[myConnectionString];
try
{
conn.Open[]; doSomething[conn];

} finally {

conn.Close[];
}

Hoặc

using [SqlConnection conn = new SqlConnection[myConnectionString]]

{

conn.Open[]; doSomething[conn];
}

Ví dụ trên ta đã có một lệnh đóng kết nối tường minh conn.Close[], tuy nhiên ví dụ sau lại làm cho trình dịch tự động sinh ra lời gọi tới lệnh conn.Dispose[] khi đóng [}] block.

Khi trả về một kết nối từ một phương thức lớp – phải chắc chắn rằng bạn đã giữ cục bộ tại đó và thực hiện lời gọi đóng cho chính nó – phương thức Close[]. Ví dụ sau cho thấy ta đã để lọt khe một kết nối:

OleDbCommand cmd new OleDbCommand[myUpdateQuery, getConnection[]]; intres = cmd.ExecuteNonQuery[]; getConnection[].Close[];

Bạn tưởng rằng lệnh getConnection[] đóng kết nối cho bạn, nhưng thực ra nó sinh ra một kết nối mới và đóng chính kết nối mới đó, còn kết nối trước đó vẫn được mở.

Nếu bạn sử dụng SqlDataReader, OleDbDataReader, v.v…, hãy đóng chúng. Thậm chí, mặc dù việc chúng tự đóng kết nối nghe có vẻ lừa đảo, bạn hãy cố gắng đóng các đối tượng này mỗi khi sử dụng chúng.

Cuối cùng, đừng bao giờ đặt các lệnh Close[] hoặc Dispose[] trong các hủy tử [Destructor] của lớp hoặc phương thức Finaliz. Nó không chỉ chẳng có giá trị gì mà còn gây cản trở hoạt động của GC và cóthể gây lỗi.

Kiểm tra thay đổi

Chỉ có cách nhận biết hiệu quả thực sự những thay đổi của bạn trong hoạt động tạo vùng kết nối chung đó là kiểm thử tải ứng dụng của bạn. Nếu bản có thể thực hiện unit test, hãy tận dụng bằng cách thử chúng trong các vòng lặp để kiểm thử khả năng chịu đựng lỗi của ứng dụng. Nếu không thể unit test, hãy sử dụng công cụ test tải của Web. Bạn có thể dùng thử các công cụ như OpenSTA tại linkt [Only registered and activated users can see links. ]. Thường thì ứng dụng bị lỗi không giúp bạn định vụ được lỗi ở đâu, bạn cần thực hiện kiểm tra tải của từng modul một để phát hiện và fix lỗi.

Kiểm soát các hoạt động liên quan Connection Pool

Cách dễ dàng nhất để kiểm soát số lượng kết nối đến CSDL là sử dụng Performance Monitor có sắn trong các công cụ Administrative của Windows. Nếu bạn đang chạy MS SQL Server, hãy thêm vào bộ đếm hiệu suất kết nối người dùng vào SQL Server General Statistics [User Connection]. Bộ đếm này có sẵn trong máy SQL Server vì vậy bạn chỉ cần gõ địa chỉ IP trong Select Counters From Computer box. Một cách khác là truy vấn trong DBMS. Ví dụ trong SQL Server chạy:

EXEC SP_WHO

trong Oracle, chạy:

SELECT * FROM V$SESSION WHERE PROGRAM IS NOT NULL

Bạn sẽ làm gì khi phát hiện Connection Pool có vấn đề mà không thể dừng nó lại để khắc phục sửa chữa, kể cả khi đã khởi động lại IIS. Bộ nhớ và tài nguyên CPU của bạn vẫn bị chiếm giữ và phình ra, chỉ có mỗi một cách là “Turn Off”Connection Pool.

Ta cùng xem xét đoạn code sau:

conn = new SqlConnection[];

try {

conn.ConnectionString = “integrated security=SSPI;SERVER=YOUR_SERVER;DATABASE=YOUR_DB_ NAME;Min Pool Size=5;Max Pool Size=60;Connect Timeout=2;”; // Chú ý Connection Timeout được đặt // chỉ 2 giây! conn.Open[];

} catch[Exception] {

if [conn.State != ConnectionState.Closed] conn.Close[]; conn.ConnectionString = “integrated security=SSPI;SERVER=YOUR_SERVER;DATABASE=YOUR_DB_ NAME;Pooling=false;Connect Timeout=45;”; conn.Open[];
}

Khi lập trình để tránh hiện tượng không thể giải phóng được Connection Pool khi ứng dụng gặp lỗi, ta sử dụng giải pháp này. Nghĩa là, nếu ta không thể mở được một kết nối trong Connection Pool trong TimeOut 2 giây, ta sẽ yêu cầu mở một kết nối mới mà không dùng Pool, Pool lúc này đã được “Turn Off”.

Chủ Đề