Creating a FTP Connection Pool using Object Pool Pattern in C#
One of my web applications was using FTP to upload photos to a FTP server. A new connection was created every time a user uploaded his photos. The instantiation of a new connection on every upload request was turning out to be a very costly step. So I decided to use the Object Pool Pattern to implement a pool of FTP Connections.
Here's the FtpClientConnectionPool class. This code is based on an example I found on Best-Practice Software Engineering.
1 public class FtpClientConnectionPoolI have implemented this as a Singleton because I did not want multiple instances of the connection pool floating around. The myInstance variable is declared to be volatile to ensure that assignment to the instance variable completes before the instance variable can be accessed.
2 {
3 private static volatile FtpClientConnectionPool myInstance;
4 private static object syncRoot = new Object();
5 private Listlocked, unlocked;
6
7 private FtpClientConnectionPool()
8 {
9 locked = new List();
10 unlocked = new List();
11 }
12
13 public static FtpClientConnectionPool Instance
14 {
15 get
16 {
17 if (myInstance == null)
18 {
19 lock(syncRoot)
20 {
21 if (myInstance == null)
22 myInstance = new FtpClientConnectionPool();
23 }
24 }
25 return myInstance;
26 }
27 }
28
29 public FtpClient CheckOut()
30 {
31 lock(syncRoot)
32 {
33 for (int i = unlocked.Count - 1; i >= 0; i--)
34 {
35 FtpClient f = unlocked[i];
36 unlocked.Remove(f);
37 if (!TimedOut(f))
38 {
39 locked.Add(f);
40 return f;
41 }
42 }
43 // No active instance - create new
44 FtpClient newF = Create();
45 locked.Add(newF);
46 return newF;
47 }
48 }
49
50 public void CheckIn(FtpClient f)
51 {
52 lock(syncRoot)
53 {
54 locked.Remove(f);
55 unlocked.Add(f);
56 }
57 }
58
59 private FtpClient Create()
60 {
61 FtpClient newInstance = new FtpClient(
62 Settings.FTPServer,
63 Settings.FTPUsername,
64 Settings.FTPPassword);
65 newInstance.ChangeDir("/");
66 return newInstance;
67 }
68
69 private Boolean TimedOut(FtpClient f)
70 {
71 try
72 {
73 f.ChangeDir("/");
74 }
75 catch
76 {
77 return true;
78 }
79
80 return false;
81 }
82 }
To make this work for the multithreaded web scenario, the approach uses a syncRoot instance to lock on, rather than locking on the type itself, to avoid deadlocks.
Note that there is no limit to the number of connections in this implementation. The pool will grow with the number of simultaneous users, and shrink as the connections timeout.
A client will use this connection pool as:
1 FtpClient ftp = FtpClientConnectionPool.Instance.CheckOut();
2 ftp.Upload(file);
3 FtpClientConnectionPool.Instance.CheckIn(ftp);
3 comments:
Nice article !But where will you close the FTP Connections ?Do we need to do that in the Application_Stop event ?
Indeed it looks like this code relies on automatic connection loose by timeout.
There obviously is no active closing: On connection request ("CheckOut"), the pool simply checks all connections still present for the first one still active (and meanwhile cleans up all inactive which are found so far) and automatically creates a new one when no active one is left.
On connection release ("CheckIn"), the connection is simply moved from the "in use" list to the "currently not in use" list, waiting there for better times or the next request.
Thats quite simple but not quite clean and may cause conflicts in restrictive operational environments where rules exist which inhibit idle connection hold.
Thus the problem / question "how to regularly and actively cleanup aged connections" is not addressed here as it seems.
ZipwiZ
Sometimes it is helpful to dig deep...
This article refers primarily to:
http://best-practice-software-engineering.ifs.tuwien.ac.at/patterns/objectpool.html
Nice thing. It in turn promarily refers to:
http://www.javaworld.com/javaworld/jw-06-1998/jw-06-object-pool.html
Thats the root source. Looking on page 3 of this article shows that the author in 1998 was aware of the problem:
http://www.javaworld.com/javaworld/jw-06-1998/jw-06-object-pool.html?page=3
Now we have a C# example here, but since the sources are Java based, I dare to refer to:
http://commons.apache.org/pool/
This library seems to cover all problems omitted here for simplicity, including regular cleanup by a separate thread etc. I do not know how simple or complicated it may be to translate this to C# / .NET, but it should be feasible -- and maybe there is already some kind of "Apache commons for .NET" out there. Don't know -- for the Spring lib set, there is...
ZipwiZ
Post a Comment