2016-01-27 16 views
7

Tôi có điểm cuối dịch vụ web sử dụng tài nguyên có thể thay đổi được từ thư viện Java. Điểm cuối dịch vụ web đó có thể nhận nhiều truy vấn cùng một lúc. (điểm cuối được thực hiện bằng cách sử dụng Ring/Compojure). Việc tạo ra các tài nguyên này là tốn kém, vì vậy việc tạo lại chúng cho mọi cuộc gọi dịch vụ web thực sự không hiệu quả.Cách tốt nhất để quản lý một nhóm tài nguyên ở Clojure

Điều tôi muốn làm là tạo một tài khoản pool mà tôi điền khi dịch vụ web bắt đầu. Sau đó, mỗi lần điểm cuối được gọi, nó lấy một tài nguyên từ hồ bơi, sử dụng nó để xử lý của nó, và sau đó đẩy nó trở lại vào hồ bơi và chờ cuộc gọi tiếp theo xảy ra.

Tôi tự hỏi điều gì sẽ là cách tốt nhất để làm điều đó trong Clojure? Có thư viện Clojure "hồ bơi" có thể giúp tôi với điều đó không?

Tôi ngây thơ đã cố gắng thực hiện điều đó bằng cách sử dụng một vector trong một nguyên tử trong đó mỗi mục của vectơ là tài nguyên đó. Tuy nhiên, nó nhanh chóng học được rằng nó không thể thực sự làm việc theo cách đó.

+0

Nếu bạn không nhớ một số hạng nặng Java interop, các [Apache Commons Pool] (https://commons.apache.org/proper/commons- hồ bơi /) thư viện luôn ở đó. – ez121sl

Trả lời

3

này được dựa trên ý tưởng của việc sử dụng refs Timothy Pratley 's:

(def pool (ref ['a 'b 'c])) 

(defn take' [pool] 
    (dosync 
    (let [[h & t] @pool] 
     (ref-set pool (vec t)) 
     h))) 

(defn put [pool x] 
    (dosync 
    (alter pool conj x) 
    nil)) 

(take' pool) ;; => 'a 
(put pool 'a) ;; => nil 
(take' pool) ;; => 'a 
(take' pool) ;; => 'b 
(take' pool) ;; => 'c 

Có lẽ không phải là cách tốt nhất để tấn công này. Nhưng tôi thích sự đơn giản của nó.

+0

Tuyệt vời, đây chính xác là những gì được yêu cầu, cảm ơn! – Neoasimov

2

Để thực hiện một hồ bơi bạn cần phải giải quyết 2 vấn đề:

  1. Concurrency. Sử dụng lockinghttps://clojuredocs.org/clojure.core/locking hoặc ref thay vì nguyên tử. Yêu cầu có thể được đồng thời, vì vậy bạn cần phải cẩn thận rằng hai người tiêu dùng không thể nhận được cùng một tài nguyên.

  2. Phát hành tài nguyên. Hãy xem xét sử dụng một mẫu như (with-open ...) tức là một macro mở rộng đến một cố gắng cuối cùng, nơi tài nguyên được phát hành trở lại hồ bơi mở khi bạn rời khỏi phạm vi khối.

Bạn có thể muốn chỉ định trạng thái 'lỗi' cũng như 'có sẵn' hoặc 'đang sử dụng', nơi tài nguyên có thể cần được phát hành và tạo lại.

+0

Có thể sử dụng ref và không bao gồm công việc thực tế của tài nguyên trong giao dịch không? Bởi vì thử lại trên đó sẽ là không mong muốn. Một ví dụ sẽ là tuyệt vời. – muhuk

+0

Có thể không có io (hoặc tác dụng phụ) được thực hiện trong một giao dịch. Tôi đã không đề xuất làm công việc trong giao dịch, nhưng thay vì sử dụng ref hoặc khóa để buộc tính nhất quán khi truy cập danh sách tài nguyên, lấy chúng và giải phóng chúng. Bạn nên tránh khóa bất cứ thứ gì trong khi tài nguyên đang được sử dụng vì nó có khả năng có thể chặn trong một thời gian dài. Chắc chắn tôi có thể thêm một ví dụ sớm. –

+0

Ồ tôi thấy bạn đã làm một ví dụ, tuyệt vời :) –

2

Có giao diện này kul/pool. Nó sử dụng Apache Commons Pool. Tôi hy vọng nó hữu ích.

+0

Sẽ ghi nhớ điều đó, nhưng tôi nghĩ tôi thích cách thành ngữ trong clojure hơn, cảm ơn! – Neoasimov

0

Bạn cũng có thể sử dụng một nguyên tử:

(def pool (atom ['c 'b 'a])) 

(defn take' 
    [pool] 
    (loop [] 
    (let [p @pool] 
     (if (compare-and-set! pool p (pop p)) 
     (peek p) 
     (recur))))) 

(defn put 
    [pool x] 
    (swap! pool conj x) 
    nil) 

(take' pool) ;; => 'a 
(put pool 'a) ;; => nil 
(take' pool) ;; => 'a 
(take' pool) ;; => 'b 
(take' pool) ;; => 'c 
Các vấn đề liên quan