EF6学习笔记二十六:连接弹性

要专业系统地学习EF推荐《你必须掌握的Entity Framework 6.x与Core 2.0》。这本书作者(汪鹏,Jeffcky)的博客:https://www.cnblogs.com/CreateMyself/

连接弹性这一节,作者说的不是很多,而且又出现一个polly库,感觉弄下去真的要花不少时间。

这一块我目前没有准备继续去弄,也就简单记录一下我从这一节内容中学到的东西吧。

连接弹性,什么意思呢?就是你数据库的连接要是断开了,那么还有其他的机制还对这一情况做处理。如果说连接一断就随他去了,显然不够有“弹性”。

说到这一点我想到了我在学习node.js时用到的一个启动器,叫forever,当然启动器也有很多种。什么意思呢?如果说你的服务跑起来了,不用启动器的情况,一个小的错误就能造成你服务关闭。那么我用启动器来启动我的项目就不会出来这种情况。

一般很多部署node程序会用到pm2,这个也差不多就是这种功能,它有一个守护进程,让你的网站不至于一个错误就停止了。

所以我就弄不懂IIS中怎么就没听说过有什么启动器。node里面学的东西其实没多少,就是使用各种包,也不用去管具体实现。有点没劲。最有用的就是它和.net程序之间的差异能够让人引发很多思考。

说回EF中连接弹性的问题。其实如果说数据库连接断开了,可能会有很多种情况,可能因为是网络问题啊。那么我们能够想到的应对方法就是去重试。

EF提供了大量有关暂时异常的信息。如果由于任何原因与数据源的连接丢失并且未达到重试限制,那么它将会以和禁用连接弹性相同的方式引发异常。

EF6.X上给与了一下4种连接策略

1.DefaultExceptionStrategy:默认执行策略,当失败时,对除SQL Server 以外的数据库不会进行重试。

2.DefaultSqlExecutionStrategy:根本不会重试,但是会包含任何可能的短暂异常,以通知开发者可能需要启用连接弹性,次类受保护限制。

3.DbExecutionStrategy:此类适用于其他执行策略的基类,以指数实现重试策略,其中初始重试以0计数,并且延迟指数增加,知道命中最大重试计数。次类具有抽象的ShouldRetryOn方法,可以在派生执行策略中实现,以控制应重试哪些异常。

4.SqlAzureExecutionStrategy:该执行策略继承自DbExecutionStrategy,并将重试在使用SqlAzure时可能一直出现的异常。

上面的是书中的原话,我还是得用自己的话说一下,数据源连接丢失,EF默认是有重试的。但是我按照书上的代码写出来,有一些疑问。

来看一下写一个派生自DbExecutionStrategy,首先我了解到,数据库连接丢失,EF会调用哪个方法,那就知道了有这个切入点吧.

ShouldRetryOn方法中我只是加了一句console,还是执行的基类的代码

 public class SqlServerExecutionStrategy : DbExecutionStrategy
    {
        public SqlServerExecutionStrategy()
        {

        }

        /// <summary>
        /// 确定指定的异常是否表示临时故障   如果指定的异常被认为是暂时的,则为true,否则为false。
        /// </summary>
        /// <param name="maxRetryCount">最大重试次数</param>
        /// <param name="maxDelay">最大等待时长</param>
        public SqlServerExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, maxDelay)
        {
            
        }

        protected override bool ShouldRetryOn(Exception exception)
        {
            //  同样的是打印四次
            Console.WriteLine("777777777777777777777777777");
            throw new NotImplementedException();
        }
}
View Code

写一个派生自DbConfiguration的类,在EF中注册配置。

public class EFConfiguration : DbConfiguration
    {
        public EFConfiguration()
        {
            SetExecutionStrategy(SqlProviderServices.ProviderInvariantName, () => new SqlServerExecutionStrategy(3, TimeSpan.FromSeconds(5)));
        }
    }
View Code

然后我关闭sqlserver服务,这样它将连接不到数据库

可以看到打印了4次,那意思是重试连接了4次吗?

 在EFConfiguration传递的3和5,意思是重试次数为3次,最大延迟时间为5秒,那么我改成重试1次,还是一样,这个方法被调用4次。所以我就怀疑这是不是有问题?

如果开启sqlserver服务,这个方法一次都不会被调用

途中我碰到一个问题,就是我的sqlsever服务断开之后,运行程序,控制台过了很久才出现内容,为什么等待了那么久?我突然想到连接字符串里面有个Connect Timeout属性,默认是30秒.于是我改成5秒,发现确实是这里的问题。

 

接着来看作者在shouldRetryOn方法中写的代码,我加了三句console

 protected override bool ShouldRetryOn(Exception exception)
        {
            Console.WriteLine("11111111111111111111111111111111111111111");
            bool bRetry = false;
            if (exception is SqlException objSqlException)
            {
                Console.WriteLine("222222222222222222222222222222222");
                var lstErrorNumbersToRetry = new List<int>() { 5 };
                if (objSqlException.Errors.Cast<SqlError>().Any(a => lstErrorNumbersToRetry.Contains(a.Number)))
                {
                    Console.WriteLine("33333333333333333333333333333");
                    bRetry = true;
                }
            }
            return bRetry;
        }
View Code

同样关闭Sqlserver服务来执行看一下

shouldRetryOn这个方法返回的是bool类型,如果异常是短暂的返回true,否则false,刚刚的打印内容可以看到这个异常被认为不是暂时的。

作者的判断依据是"5"这个数字,但是5代表的是什么呢?

转到Number定义看一下

 

 通过这几句注释完全看不到什么有价值的东西。

幸好.net开源了,那就去MSDN上看一下:https://docs.microsoft.com/zh-cn/dotnet/api/system.data.sqlclient.sqlerror.number?view=netframework-4.7.2#System_Data_SqlClient_SqlError_Number

它说SqlError.Number类型的值对应master.dbo.sysmessages表,那就去查表吧

 

 

 

 但是根本就没有5啊,最小都是21,。我其实也把异常里面的Number看了一下,发现是2

protected override bool ShouldRetryOn(Exception exception)
        {
            Console.WriteLine("11111111111111111111111111111111111111111");
            bool bRetry = false;
            if (exception is SqlException objSqlException)
            {
                Console.WriteLine("222222222222222222222222222222222");
                var lstErrorNumbersToRetry = new List<int>() { 5 };
                var abc = objSqlException.Errors.Cast<SqlError>();
                var str = "";
                foreach (var item in abc)
                {
                    str += item.Number + ",";
                }
                var str2 = str;  //  2,
                if (objSqlException.Errors.Cast<SqlError>().Any(a => lstErrorNumbersToRetry.Contains(a.Number)))
                {
                    Console.WriteLine("33333333333333333333333333333");
                    bRetry = true;
                }
            }
            return bRetry;
        }
View Code

 

行,到此为止,这个对我来说已经进行不下去了。

总结一下我通过写一个派生自DbExecutionStrategy类有什么作用呢?作用就是知道数据库连接丢失,shouldRetryOn方法会被调用,这一点是肯定的,那么其他的东西不怎么明白也就不妄下结论了

这就是简单的连接弹性。现在来说道Polly库。

这个是做什么的呢?其实我觉得就是对异常处理做出的一种规范。

数据库连接断开,只有重试吧?原因有很多种,很有可能他就一直断开了。所以Polly他为你是解决不了的。你说你自己都不知道发生什么问题,polly又能怎么办。

我刚开始就是认为,用这个那一定是不管什么情况下的连接丢失,polly都能帮我解决,这就不对了。

我觉得Polly就是提供了对异常处理的规范,它有很多种策略,可以参考Jeffcky的一篇博客:https://www.cnblogs.com/CreateMyself/p/7589397.html

行吧,就这了。我开始想把polly那些东西再写一下的。不过想到也没有太了解,也就没有耐心了。

 

posted @ 2019-02-03 00:05  张四海  阅读(434)  评论(0编辑  收藏  举报