对象池设计模式关闭所有空闲连接

时间:2018-04-21 08:45:00

标签: c# mysql visual-studio object-pooling

这是一个关于使用MySql Connection的对象池设计模式的问题。实现此设计模式的目的是在需要创建多个实例时重用相同的连接/对象。下面的代码是一类用c#编写的对象池设计模式。

public abstract class ObjectPool<T>
{
    private long expirationTime;
    private Dictionary<T, long> Running, Idle;

    //Constructor
    public ObjectPool()
    {
        expirationTime = 30000; //30 seconds
        Running = new Dictionary<T, long>();
        Idle = new Dictionary<T, long>();
    }

    protected internal abstract T create();

    public abstract Boolean validate(T o);

    public abstract void expire(T o);

    public virtual T GetObject
    {
        get
        {
            //lock the first thread 
            //make sure first thread is executed before the second thread
            lock (this)
            {
                long now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
                T t;
                if (Idle.Count > 0)
                {
                    IEnumerator<T> element = Idle.Keys.GetEnumerator();
                    while (element.MoveNext())
                    {
                        t = element.Current;

                        //unlocked[t] -> Get the VALUE asscoiated with the specified KEY
                        if ((now - Idle[t]) > expirationTime)
                        {
                            //object has expired
                            System.Diagnostics.Debug.WriteLine("object has expired");
                            Idle.Remove(t);
                            expire(t);
                            //to obtain the default value of a Generic Type
                            t = default(T);
                        }
                        else
                        {
                            if (validate(t))
                            {
                                System.Diagnostics.Debug.WriteLine("Connection is still open. used back the same object");
                                Idle.Remove(t);
                                Running.Add(t, now);
                                return (t);
                            }
                            else
                            {
                                //object failed validation
                                System.Diagnostics.Debug.WriteLine("object has failed validation");
                                Idle.Remove(t);
                                expire(t);
                                t = default(T);
                            }
                        }
                    }
                }

                // no objects avaialable, create a new one
                System.Diagnostics.Debug.WriteLine("no object is available, creating a new one");
                t = create();
                Running[t] = now;
                return (t);
            }
        }
    }

    public virtual void releaseObject(T t)
    {
        lock (this)
        {
            Running.Remove(t);
            Idle[t] = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            System.Diagnostics.Debug.WriteLine("release object");
        }
    }
}    

上面的代码是对象池设计模式的实现,每次通过调用此函数创建一个新实例&#34; GetObject&#34;,下面的类是一个覆盖ObjectPooling方法的类并与MySql集成

class DatabaseConnection : ObjectPool<MySqlConnection>
{
    private String ConnectionString =
        "datasource=127.0.0.1;" +
        "port=3306;" +
        "username=root;" +
        "password=;" +
        "database=mesa;" +
        "Integrated Security = SSPI;"+
        "Min Pool Size = 1;" +
        "Max Pool Size = 9;" +
        "Connection Lifetime = 600;";

    public DatabaseConnection() : base()
    {
    }

    public override void expire(MySqlConnection o)
    {
        try
        {
            o.Close();
            System.Diagnostics.Debug.WriteLine("Closing Database Connection Successful");
        }
        catch (MySql.Data.MySqlClient.MySqlException ex)
        {
            System.Diagnostics.Debug.WriteLine("Closing Database Connection Unsuccessful");
            MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
                            "MySql Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        throw new NotImplementedException();
    }

    public override bool validate(MySqlConnection o)
    {
        System.Diagnostics.Debug.WriteLine("Validating");
        try
        {
            if (o.State.ToString() == "Open")
            {
                return true;
            }
        }
        catch (MySql.Data.MySqlClient.MySqlException ex)
        {
            System.Diagnostics.Debug.WriteLine("Database cannot check current state");
            MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
                            "MySql Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

            return false;
        }

        throw new NotImplementedException();
    }

    protected internal override MySqlConnection create()
    {
        try
        {
            MySqlConnection mySqlConnection = new MySqlConnection(ConnectionString);
            mySqlConnection.Open();
            System.Diagnostics.Debug.WriteLine("Database Connection Successful");
            return (mySqlConnection);
        }
        catch (MySql.Data.MySqlClient.MySqlException ex)
        {
            MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
                            "MySql Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

            return null;   
        }

        throw new NotImplementedException();
    }
}

上面的类说明了三种覆盖方法,它们是&#34;创建&#34;这将在连接过期时创建一个新实例,并且&#34;到期&#34;这是在连接已经过期时关闭连接。最后一个验证当前连接是否打开的函数。现在,下面的代码是一个普通的类,它将与对象池和数据库连接相关联。

class InputValidation
{
    private DatabaseConnection databaseConnection = new DatabaseConnection();

    //Constructor
    public InputValidation()
    {

    }

    //Destructor
    ~InputValidation()
    {
      //databaseConnection.expire();
    }


    public void CheckForExistingPT_NUM1(String pt)
    {
        MySqlConnection connection1 = databaseConnection.GetObject;

        try
        {
            string sql = "SELECT * FROM carrier";
            MySqlCommand cmd = new MySqlCommand(sql, connection1);
            MySqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                string someStringFromColumnZero = reader.GetString(0);
                string someStringFromColumnOne = reader.GetString(1);
                System.Diagnostics.Debug.WriteLine(someStringFromColumnZero + "," + someStringFromColumnOne);
            }

            reader.Close();
        }
        catch (MySql.Data.MySqlClient.MySqlException ex)
        {

            MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
                            "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        databaseConnection.releaseObject(connection1);
    }

    public void CheckForExistingPT_NUM2(String pt)
    {
        MySqlConnection connection1 = databaseConnection.GetObject;

        try
        {
            string sql = "SELECT * FROM carrier";
            MySqlCommand cmd = new MySqlCommand(sql, connection1);
            MySqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                string someStringFromColumnZero = reader.GetString(0);
                string someStringFromColumnOne = reader.GetString(1);
                System.Diagnostics.Debug.WriteLine(someStringFromColumnZero + "," + someStringFromColumnOne);
            }

            reader.Close();
        }
        catch (MySql.Data.MySqlClient.MySqlException ex)
        {

            MessageBox.Show("Error " + ex.Number + " has occurred: " + ex.Message,
                            "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        databaseConnection.releaseObject(connection1);
    }

}

现在,正如您从这两个函数CheckForExistingPT_NUM1(String pt)CheckForExistingPT_NUM2(String pt)中看到的那样,实际上这两个函数只是使用相同的连接来与数据库交互,而不是关闭连接并再次打开新连接。下面是实现所有逻辑的主类的函数。

private void AliasButton_Click(object sender, EventArgs e)
{
        String PT_NUM = PTNumTextBox.Text;

        InputValidation inputValidation = new InputValidation();

        inputValidation.CheckForExistingPT_NUM1(PT_NUM);

        inputValidation.CheckForExistingPT_NUM2(PT_NUM);

}

以下是在输出框中打印的消息行

no object is available, creating a new one
The thread 0x994 has exited with code 0 (0x0).
Database Connection Successful
801BRWUE,BCM88682CA1KFSBG
U10BRT7E,BCM3450KMLG
release object
Validating
Connection is still open. used back the same object
801BRWUE,BCM88682CA1KFSBG
U10BRT7E,BCM3450KMLG
release object

这里的问题是当没有创建实例时连接将保持空闲状态。从输出框中可以看出,connection2仍处于空闲状态,创建新实例时连接将被破坏,如何关闭所有空闲连接?感谢

1 个答案:

答案 0 :(得分:0)

这段代码似乎没必要,因为MySQL Connector / NET已经implements its own connection poolingMySqlConnection对象只是底层套接字连接的轻量级包装器;缓存和重用它们几乎没有什么好处。 (您可能会通过创建可终结的InputValidation对象来增加更多开销,然后通过重用MySqlConnection来保存。)

  

如何关闭所有空闲连接?

也许创建一个Timer来定期清理空闲连接,比如每分钟一次?但我的建议是不要创建这个对象池,只依赖于内置的连接池。

请注意,由于Connector / NET的连接池,与MySQL服务器的基础网络连接将保持打开状态。您可以调用MySqlConnection.ClearAllPools()来关闭所有这些池连接(但请注意,这将使打开下一个数据库连接的时间明显延长)。