Поддерживайте действительные подключения к БД без использования собственного пула - PullRequest
0 голосов
/ 09 ноября 2011

Есть ли способ сохранить соединения с БД открытыми и готовыми к использованию во время выполнения? Наш сервис WCF должен поддерживать связь таким образом. Теперь у нас есть код, похожий на этот кусок кода:

// This is the class of objects that has connections within.
[DataContract]
public class SomeObj
{
    [DataMember]
    public string Name { get; set; }

    public OracleConnection Conn { get; set; }
}

// Dictionary of SomeObjs
private static Dictionary<string, SomeObj> myObjs = new Dictionary<string, SomeObj>();

Когда служба запускается, она также соединяет все базы данных из собственных файлов конфигурации со строками подключения внутри. Но, как вы знаете, соединения могут стать поврежденными и непригодными для использования или даже быть недоступными в конце. Поэтому для устранения этой проблемы у нас есть специальный таймер, который проверяет все соединения в конфигурации:

    // Setup timer method. Timer will fire every minute.
    static private void CreateTimer()
    {
        Timer Timer1 = new System.Timers.Timer();
        Timer1.Enabled = true;
        Timer1.Interval = 60000;
        Timer1.Elapsed += new System.Timers.ElapsedEventHandler(Timer1_Elapsed);
    }

    static private void Timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
            Parallel.ForEach(Program.config.Root.Elements("databases"), el =>
            {
                try
                {
                    Program.OpenConnection(el.Attribute("name").Value);
                }
                catch (Exception exc)
                {
                    Console.WriteLine(DateTime.Now.ToString() + " " + exc);                        
                }
            });

            // This is old way we checks connections consistently
            //foreach (XElement element in Program.config.Root.Elements("databases"))
            //{
            //    try
            //    {                
            //        AServ.OpenConnection(el.Attribute("name").Value);                
            //    }
            //    catch (Exception exc)
            //    {
            //        Console.WriteLine(DateTime.Now.ToString() + " " + exc);                
            //    }
            //}                
    }


    public static bool OpenConnection(string name)
    {
        // if connections exactly doesn't exists
        if (!myObjs.ContainsKey(name))
            {
                XElement el = (from t in config.Root.Elements("databases")
                               where t.Attribute("name").Value == name
                               select t).FirstOrDefault();
                if (el == null)
                {
                    lock (myObjs)
                    {
                        myObjs.Remove(name);                            
                    }
                    return false;
                }
                lock (myObjs)
                {
                    myObjs.Add(name, new SomeObj { Conn = new OracleConnection { ConnectionString = el.Attribute("connectionString").Value } }, Name = el.Attribute("name").Value);
                }
            }
            // if connection broken
            if ((myObjs[name].Conn.State != ConnectionState.Open || !myObjs[name].Conn.Ping()) && myObjs.ContainsKey(name))
            {
                try
                {
                    // Trying to get it alive 
                    lock (myObjs)
                    {
                        myObjs[name].Conn.Close();
                        myObjs[name].Conn.Open();
                    }
                }
                catch (Exception e)
                {
                    if (!myObjs.ContainsKey(name))
                        return false;

                    lock (myObjs)
                    {
                        myObjs.Remove(name);
                    }

                    return false;                        
                }
                Console.WriteLine(DateTime.Now.ToString() + " " + myObjs[name].Conn.ConnectionString);                    
            }

        return true;
    }

И еще одна вещь, которую вы должны знать, ребята, как мы используем хранение соединений в самом методе:

    private static int GetParameterValue(string name, int id)
    {
        // if we don't get parameter value at the moment because broken connection
        if (!OpenConnection(name))
            return -1;

        // assuming connection is OK and getting the parameter value
        string parameterQuery = @"select GetInfo(:id) from dual";
        OracleCommand parameterCommand = new OracleCommand(parameterQuery, myObjs[name].Conn);
        parameterCommand.Parameters.Add("id", id);            
        return Convert.ToInt32(parameterCommand.ExecuteScalar());
    }

Проблема в в том, что иногда первая проверка таймера не может быть выполнена вовремя, а вторая проверка таймера начинается, пока выполняется первая . Это дает некоторые странные ошибки с утечками памяти кучи (я так думаю). Другая проблема заключается в в том, что даже мы разрабатываем шаблон одноэлементного проектирования, чтобы иметь только одну проверку во времени, проверка может зависнуть, а другие проверки никогда не будут запущены (потому что открытие соединения занимает много времени с завышенной ценой для некоторых баз данных. Не знаю, почему это происходит, но мы пытались добавить Connection timeout=30;, но, честно говоря, я не помню, давал ли он правильный результат или нет. Конечно, нет.)

Тогда этот сервис, запущенный во времени, занимает столько памяти, сколько он работает. Я пробовал много манипуляций с этим кодом, даже однажды столкнулся с мертвыми блокировками где-то в коде (почти 100% загрузка процессора).

Да, я знаю о пуле ADO .NET, но мы не можем его использовать, потому что у нас огромная база унаследованного кода, которая работает, как показано выше, и должна каким-то образом улучшить существующую логику проверки.

Так скажите мне, что вы думаете, как мы можем установить живое соединение во времени без объединения? Мне нужен какой-то способ, который может использовать как можно меньше памяти (существующий код требует больше памяти, чем работает), и работать быстро с небольшими изменениями кода.

Любая помощь очень ценится! Очень надеюсь на вашу помощь в работе с stackoverflow!

Я думаю об использовании специального потока, который будет проверять соединения в цикле с Thread.Sleep(60000) в конце. Но я не уверен в этом.

Поставщик ADO .NET Devart dotConnect для Oracle . База данных Oracle 9-11 (зависит от сервера), C ​​# .NET Framework 4.

1 Ответ

1 голос
/ 09 ноября 2011
  1. Найдите дом своего оракула в этом разделе реестра HKLM \ SOFTWARE \ ORACLE (например, C: \ oracle \ client \ 10g)
  2. откройте каталог network \ admin в вашем ORACLE_HOME (например, C: \ oracle \ client \ 10g \ network \ admin)
  3. открыть (или создать, если он еще не существует) файл с именем sqlnet.ora
  4. добавить следующую строку: SQLNET.EXPIRE_TIME = 1

Что произойдет, так это то, что зонд будет отправляться каждую 1 минуту на сервер БД, что приведет к тому, что соединение останется живым.

Надеюсь, это поможет. Daniel

...