2012-01-06 31 views
8

Tôi có dịch vụ .NET 4 C# đang sử dụng thư viện TPL để phân luồng. Gần đây chúng tôi đã chuyển sang sử dụng kết nối tổng hợp, vì một kết nối đã trở thành một cổ chai để xử lý.Kết nối cơ sở dữ liệu tổng hợp với dịch vụ đa luồng

Trước đây, chúng tôi đã sử dụng mệnh đề khóa để kiểm soát an toàn luồng trên đối tượng kết nối. Khi công việc sẽ sao lưu, hàng đợi sẽ tồn tại dưới dạng nhiệm vụ, và nhiều luồng (nhiệm vụ) sẽ đợi trong mệnh đề khóa. Bây giờ, trong hầu hết các kịch bản, các chủ đề chờ IO cơ sở dữ liệu và các quy trình làm việc MUCH nhanh hơn.

Tuy nhiên, bây giờ tôi đang sử dụng tính năng tổng hợp kết nối, chúng tôi có sự cố mới. Khi đạt đến số lượng kết nối tối đa (100 mặc định), nếu có yêu cầu kết nối thêm, có thời gian chờ (xem Pooling info). Khi điều này xảy ra, một ngoại lệ được ném ra rằng "Yêu cầu kết nối đã hết thời gian".

Tất cả IDisposables của tôi đều nằm trong các câu lệnh và tôi đang quản lý đúng các kết nối của mình. Kịch bản này xảy ra do nhiều công việc được yêu cầu hơn so với hồ bơi có thể xử lý (dự kiến). Tôi hiểu lý do tại sao ngoại lệ này bị ném và tôi biết cách xử lý nó. Một thử lại đơn giản cảm thấy giống như một hack. Tôi cũng nhận ra rằng tôi có thể tăng thời gian chờ thông qua chuỗi kết nối, tuy nhiên điều đó không giống như một giải pháp vững chắc. Trong thiết kế trước đó (không gộp chung), các mục công việc sẽ xử lý vì khóa trong ứng dụng.

Cách tốt nhất để xử lý trường hợp này là gì để đảm bảo rằng tất cả công việc được xử lý?

+0

Bạn có thể đăng một số mã của bạn? Sẽ rất hữu ích khi xem bạn đang tạo và lên lịch các nhiệm vụ như thế nào. –

+0

Nhiệm vụ được tạo ra gần như thế này: Task.Factory.StartNew (() => ProcessItem (mục)); Tôi không theo dõi các đối tượng nhiệm vụ đủ để hạn chế việc tạo các nhiệm vụ (nếu đó là tuyến đường bạn đang nghĩ). Không phải tất cả các tác vụ đều yêu cầu cơ sở dữ liệu hoạt động, và không phải tất cả công việc cơ sở dữ liệu đều sử dụng cùng một cơ sở dữ liệu (có rất nhiều khác nhau, oracle, mssql, v.v.). –

+0

Đây có phải là biện pháp xử lý tải trọng tăng hoặc yêu cầu liên tục đến quá nhanh để bạn có thể xử lý không? Nếu sau này, bạn sẽ phải thừa nhận thất bại và trở về thất bại tại một số điểm. –

Trả lời

10

Cách tiếp cận khác là sử dụng mã semaphore xung quanh mã truy xuất kết nối từ nhóm (và, hy vọng, trả về chúng). Sempahore giống như một tuyên bố khóa, ngoại trừ việc nó cho phép một số người yêu cầu có thể cấu hình tại một thời điểm, không chỉ một.

Something như thế này nên làm:

//Assuming mySemaphore is a semaphore instance, e.g. 
// public static Semaphore mySemaphore = new Semaphore(100,100); 
try { 
    mySemaphore.WaitOne(); // This will block until a slot is available. 
    DosomeDatabaseLogic(); 
} finally { 
    mySemaphore.Release(); 
} 
+0

Tôi thích điều này, tôi đã không nhận ra đó là (có khả năng) dễ dàng như vậy. Tôi sẽ thử điều này và báo cáo lại. –

+1

Có, tất nhiên, một số mối nguy hiểm trong việc này. Cụ thể, semaphores là không biết gì về sự ủy thác lại, vì vậy nếu cuộc gọi DoSomeDatabaseLogic() của bạn có thể đệ quy cố gắng nhập lại semaphore, bạn có khả năng gây deadlocks. Như một ví dụ về điều đó, hãy tưởng tượng rằng 100 chủ đề đã được sử dụng khe semaphore. Sau đó, mỗi chủ đề cố gắng nhập lại, và các khối chờ đợi trên một khe cắm miễn phí (mà sẽ không bao giờ xảy ra). Bạn cần phải nhận thức được khả năng và mã một cách cẩn thận. –

+0

Phần DoSomeDatabaseLogic mà tôi đang sử dụng ở đây là một đơn vị công việc và hiện là chủ đề an toàn, vì vậy tôi không nghĩ đó là vấn đề. Trong kịch bản thử nghiệm của tôi, tôi đã thử semaphore này và nó thực sự là làm việc. Đây là những gì tôi đang tìm kiếm, cảm ơn bạn! –

2

Bạn có thể tìm kiếm để kiểm soát mức độ song song bằng cách sử dụng phương pháp Parallel.ForEach() như sau:

var items = ; // your collection of work items 
var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 100 }; 
Parallel.ForEach(items, parallelOptions, ProcessItem) 

Trong trường hợp này tôi đã chọn để thiết lập mức độ đến 100, nhưng bạn có thể chọn một giá trị có ý nghĩa cho triển khai nhóm kết nối hiện tại của bạn.

Tất nhiên, giải pháp này giả định rằng bạn có một tập hợp các mục công việc ở phía trước. Tuy nhiên, nếu bạn đang tạo Nhiệm vụ mới thông qua một số cơ chế bên ngoài như yêu cầu web đến thì ngoại lệ thực sự là một điều tốt. Tại thời điểm đó, tôi sẽ đề nghị bạn sử dụng cấu trúc dữ liệu Queue đồng thời, nơi bạn có thể đặt các mục công việc và bật chúng ra khi các luồng công nhân có sẵn.

+0

Tôi đang sử dụng Task.Factory.StartNew() để tạo các tác vụ của mình. Tôi không thấy một quá tải chấp nhận ParllelOptions như một tham số. –

+0

Cách tiếp cận trên giả định một tập hợp các đối tượng 'item' có sẵn để tạo các tác vụ' ProcessItem (item) 'cho. Điều này có thể không đại diện cho kịch bản của bạn. Cơ chế trên sử dụng 'Parallel.ForEach()' thay cho 'Task.Factory.StartNew()'. –

+0

+1, tôi đã gặp phải sự cố tương tự mà Andy đã đăng và tôi đang sử dụng Parallel.ForEach. Điều này rất hữu ích cho tôi. Tôi không biết lựa chọn như vậy cho đến khi bạn đề cập đến nó ngay bây giờ. –

0

Giải pháp đơn giản nhất là tăng thời gian chờ kết nối với khoảng thời gian bạn sẵn sàng chặn yêu cầu trước khi trả lại lỗi. Phải có một khoảng thời gian dài "quá dài".

Điều này sử dụng hiệu quả hồ bơi kết nối làm hàng đợi công việc với thời gian chờ. Nó dễ dàng hơn rất nhiều so với cố gắng để thực hiện một mình. Bạn sẽ phải kiểm tra hồ bơi kết nối là công bằng (FIFO).

+0

Tôi đề cập đến tôi đã xem xét điều này trong câu hỏi, tuy nhiên tôi không thể không cảm thấy đó là một cách xấu để xử lý nó. Nó chắc chắn sẽ giải quyết vấn đề trong ngắn hạn, nhưng có lẽ vấn đề thực sự có thể là dịch vụ là tham lam đối với công việc chế biến? Tôi không thể không lắc ý tưởng rằng với 1 kết nối nó hoạt động hoàn hảo, nếu hơi chậm. Cải thiện tốc độ xử lý quá lớn để quay lại một kết nối. –

Các vấn đề liên quan