9月 13

使用workflow来做测试

written by ocean \\ tags:

9月 09

新建了一个网站 优惠汇总网 

用的asp.net mvc+mysql的方式,第一次用asp.net mvc写代码,难免有很多走弯路的地方

开发完了调试了一下,已经可以跑在mono下了.

 

 

现在想监控一下性能,最开始是直接放在 GLobal.asax.cs里面的

        protected void Application_BeginRequest(object sender, EventArgs e)
        { 
            HttpContext.Current.Items["ExecuteTime"] = DateTime.Now; 
        }

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            if (HttpContext.Current.Items["ExecuteTime"] != null)
            {
                //todo
            }
        }

大概是这样写的

在Application_BeginRequest记录开始时间,在Application_EndRequest记录结束时间,两个一减几个日志,完事.

 

这样优点很明显,代码简单呀有木有,缺点也很明显,这样胡子眉毛一把抓,完全搞不清耗时在哪些地方.于是决定改进一下.

先看了一下miniprofiler的介绍,可惜这个只适合开发环境,我是要在生成环境部署,于是果断pass

再然后发现了nanoprofiler,发现挺好,于是添加进网站测试了一下,果然很OK,部署到mono环境,一直报404找不到.看来是不兼容呀,而且这个好像不是很流行,于是再换

这次是用的Glimpse,发现也很强大,所有东西都能查到,稍微控制下权限就能用的.但是太强大了,我又不愿意这么重,最后又走到自己开发的路子上.

 

因为我用的是Dapper,所以就做了一下修改

        public static int Execute(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
#endif
)
        {   //add by ocean
            using (new TimeMonitorForDAL(sql, param))
            {
                var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
                return ExecuteImpl(cnn, ref command);
            }
        }
        public static T ExecuteScalar<T>(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null
#endif
)
        {
             //add by ocean
            using (new TimeMonitorForDAL(sql, param))
            {
                var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
                return ExecuteScalarImpl<T>(cnn, ref command);
            }
        } 
        public static IEnumerable<T> Query<T>(
#if CSHARP30
this IDbConnection cnn, string sql, object param, IDbTransaction transaction, bool buffered, int? commandTimeout, CommandType? commandType
#else
this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, bool buffered = true, int? commandTimeout = null, CommandType? commandType = null
#endif
)
        {
               //add by ocean
            using (new TimeMonitorForDAL(sql, param))
            {
                var command = new CommandDefinition(sql, (object)param, transaction, commandTimeout, commandType, buffered ? CommandFlags.Buffered : CommandFlags.None);
                var data = QueryImpl<T>(cnn, command, typeof(T));
                return command.Buffered ? data.ToList() : data;
            }
        }

代码太多就不贴了,其实就是在执行的时候添加TimeMonitorForDAL类调用就可以了

TimeMonitorForDAL.cs

    public class TimeMonitorForDAL:IDisposable
    {

         

        DateTime start = DateTime.Now;
         
        private string Sql = string.Empty;
        private string param = string.Empty;


        public TimeMonitorForDAL(string sql, object param)
        { 
            this.Sql = sql;
            if (param != null)
            {
                this.param = JSONHelper.Serialize(param);
            }
        }

        public void Dispose()
        {
            int total = (int)DateTime.Now.Subtract(start).TotalMilliseconds;



            try
            {
                using (MySqlConnection conn = new MySqlConnection(Config.ConnectionString))
                {
                    MySqlCommand cmd = new MySqlCommand(SQL, conn); 
                    cmd.Parameters.Add("SQL", this.Sql);
                    cmd.Parameters.Add("Params", this.param);
                    cmd.Parameters.Add("ElapsedTime", total);
                    cmd.Parameters.Add("TransactionId", "");
                    cmd.Parameters.Add("AddDate", DateTime.Now);
                    conn.Open();
                    cmd.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error(ex);
            }
        } 
        private static string SQL = "insert into sys_timemonitorfordal(`SQL`,`Params`,`ElapsedTime`,`TransactionId`,`AddDate`)values(?SQL,?Params,?ElapsedTime,?TransactionId,?AddDate)";
         
    }

实现了IDisposable的接口,可以方便使用using的语法糖.

 

其实整个代码还是很简单的,这样就可以轻松实现监控sql执行耗时的功能了,而且,其它任何代码都不需要改变

当然,这个方式也是有缺点的,可能会对影响系统性能,废话,每次执行sql要多插入一次log,这个可以之后改成异步的或者一次性写入优化,不过大概的原型就这样了.

最后,贴一下sql的脚本方便备份

-- ----------------------------
-- Table structure for `sys_timemonitorfordal`
-- ----------------------------
DROP TABLE IF EXISTS `sys_timemonitorfordal`;
CREATE TABLE `sys_timemonitorfordal` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `SQL` varchar(4000) DEFAULT NULL,
  `Params` varchar(2000) DEFAULT NULL,
  `ElapsedTime` int(11) DEFAULT NULL,
  `TransactionId` varchar(100) DEFAULT NULL,
  `AddDate` datetime DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=1096 DEFAULT CHARSET=utf8;

 

另外,之所以要把log存在数据库中而不是日志中,主要是统计方便,例如下面这样

SELECT `sql`,count(*),max(ElapsedTime),Min(ElapsedTime),avg(ElapsedTime) from sys_timemonitorfordal GROUP BY `sql`

这样就可以统计出来一段时间以来执行sql的次数,最大耗时,平均耗时,做到有目的的优化sql

written by ocean \\ tags: