zoukankan      html  css  js  c++  java
  • Django 源码小剖: 更高效的 URL 调度器(URL dispatcher)

    Django 源码小剖: 更高效的 URL 调度器(URL dispatcher)

    效率问题

    django 内部的 url 调度机制说白了就是给一张有关匹配信息的表, 这张表中有着 url -> action 的映射, 当请求到来的时候, 一个一个(遍历)去匹配. 中, 则调用 action, 产生相应数据返回; 不中, 则会产生 404 等的错误, 而 django 中有内置 404 等错误响应方法.

    这种方法和 MFC 里 message map 差不多, 从项目实践(特别是配置 urls.py 文件)就可以猜到大概是这样一种工作模式.

    注意上面关于 django url 调度机制的白话描述中的「一个一个」, 这里就有效率上的问题了. 倘若业务逻辑不复杂, 且访问量不高, 系统是没有问题的; 但如果业务逻辑太复杂(直观的表述是 urls.py 中的匹配条目繁杂), 如此加之高访问量, 会加重系统的负担, 试想最坏的情况是每一个请求都会从头到尾匹配一次, 让人感到不爽.

    一种更好的解决方法

    策略还是有的, 因为业务逻辑不会经常变更, 至少不会没几分钟就变更一次, 所以可以借助哈希表来达到快速匹配的目的. 有关哈希表可以参照之前写的一篇文章: 私房STL之hash_set和hash_map

    哈希表

    一般的做法举例如下:

    http://example.com/aaaaa/
    http://example.com/bbbbb/
    http://example.com/ccccc/
    http://example.com/ddddd/
    http://example.com/eeeee/
    abcde 表示 web 应用的功能模块.

    为 aaaaa,bbbbb...都计算得到哈希值 hash(aaaaa),hash(bbbbb)...

    当请求 http://example.com/aaaaa/ 到来时候, 提取 aaaaa, 计算哈希值直接在哈希表中定位匹配条目. 一般的做法是这样, 还可以顺着这样的思路继续改进. 譬如, 存在更为复杂的功能, 而且这个功能在 aaaaa 的基础上建立起来: http://example.com/aaaaa/xxxxx, 这时候, 也可以计算 hash(aaaaaxxxxx) 加入到哈希表中.

    那是不是每个请求到来启动 web 应用程序的时候都要计算 urls.py 中的条目的哈希值? 并不需要, 可以新建一个 url 服务进程, 只专门维护哈希表, 从 urls.py 中计算哈希表; 通由内存映射技术, web 应用程序可以方便读取哈希表, 并且可以重复利用.

    什么提到会调用 url 对应的 action, 并返回响应数据, django 是怎么返回的. 我已经在 github 备份了 Django 源码的注释: Decode-Django, 有兴趣的童鞋 fork 吧.

    捣乱 2013-9-18

    http://daoluan.net

     

     

    反射的妙用_项目中的时间配置问题

     

    内容摘要

    1:阐述问题

    2:分析问题,解决问题

    3:演示解决方案

    1:阐述问题

          有时候,我们会遇上这样一个问题:有很多条件 condition1 、condition2 、condition3、condition4 、condition5......这些条件各不相同,可能同时配置其中几个,这几个条件有一个交集,交集内部就是我们需要的。

      给一个实例吧。用户在系统中配置了一个时间条件集合,用户可以按照年、月、周或者日来配置,按照其中一种来配置,下面有很多条件可以选择,其中开始日期和时间是必须配置的,最后会形成一个xml信息存储在数据库里面,我们会用当前时间判断每个用户的配置条件,如何符合,我们把他的邮箱拿出放到一个字符串尾部,不符合则不管,最后这个字符串就是所有符合用户配置条件的邮箱集合,我们可以把我们的信息推送给这些用户。其中xml按照月配置的如下:

     View Code

    StartDateTime是开始时间,它是一个基本条件,每个配置信息里面都必须存在。MonthlyDOWRecurrence是代表是按照月来配置的(也可能是WeeklyRecurrence周、DailyRecurrence日等)这个xml节点下面存在很多条件,例如WhichWeek是代表哪一周,DaysOfWeek表示每周的哪一天,又或者MonthsOfYear是每年的哪几个月。

         问题就是这样,我从数据库MatchData得到所有用户的配置的数据集 Id、Email、MatchData(sql :SELECT [Id],[Email],[MatchData]FROM [MatchData].[dbo].[EmailInTime]),MatchData就是我们的配置xml信息,从集合中找出所有符合条件的用户Email。

    2:分析问题,解决问题

         如果你遇上了啦,该如何处理呢?下面是我的处理方法,如果你有更好的办法,请给我留意,不吝赐教,谢谢!

         有人可以会总结一下有多少个条件,例如10个condition,然后写成是个if语句从上到下去判断是否存在这个条件,如果存在,判断是否当前时间符合这个条件。

     View Code

    这是一个伪代码,简单模拟了一下,大家都会发现很多问题,

    1:判断是否存在这个条件在xml里面,耗性能;

    2:有很多条件是不用去判断的,最好是xml配置文件里面有什么条件,我们就去判断什么条件;

    3:扩展性差,当我们需要条件更多的条件的时候,需要修改已有的代码。

    如何解决这三个问题呢?我们可以去遍历xml配置信息,我们遇到什么条件的时候,我们反射到封装好的的条件方法里面。遍历xml,我们就可以解决第1个问题;遇上什么条件我们判断什么条件,这个可以解决第2个问题;利用每个条件都封装成方法,遇到条件的时候反射到对应的方法,就解决了第3个问题。

    3:演示解决方案

          下面我把解决方案的数据库和代码展现给大家,希望能看到你的宝贵意见!

    数据库结构如下:

    数据库在代码的App_Data文件夹下,下载代码可以得到。

    定义好数据库后,我们看一下解决方案:

          工程下面包含三个类SqlHelper、AnalyseMatchData和MatchWithMatchData:SqlHelper类是网上下载的帮助工具类,用于操作数据库;AnalyseMatchData类只包含一个公共的方法GetMailAddressByMatchData,作为对外提供的API接口;MatchWithMatchData类是程序集内部类internal class ,因为我们一个会分层,把这下代码放在一个类库里面,这里面包含的是每个条件反射的方法。然后就是一个web.config配置文件和一个Index页面,Index页面用于测试效果,web.config配置文件里面当然就是连接字符串了啦,如果要用本人的代码就需要附加数据库和修改配置的连接字符串了,相信对大家都是小KS了。

    复制代码
     1         private readonly string connectionStr;
     2         private readonly string matchDataSqlStr;
     3         MatchWithMatchData matchFunction;//use to reflect the xml node to the corresponding function
     4         Type matchType;//the MatchWithMatchData class 's type
     5         XmlDocument xmldoc;
     6         DateTime nowDt;
     7 
     8         public AnalyseMatchData()
     9         {
    10             connectionStr = System.Configuration.ConfigurationManager.ConnectionStrings["MatchData"].ConnectionString;
    11             matchDataSqlStr = "SELECT [Id],[Email],[MatchData]FROM [MatchData].[dbo].[EmailInTime]";
    12             matchFunction = new MatchWithMatchData();
    13             matchType = matchFunction.GetType();
    14             xmldoc = new XmlDocument();
    15         }
    复制代码

    首先是定义需要的使用的全局变量,然后再构造函数里面初始化变量,细心的朋友会看到当前时间nowDt变量没有初始化,因为应该放在后面的api接口里面,当前时间应该是用户调用接口的时间。

    复制代码
     1         /// <summary>
     2         /// the api to get the email address of  match all conditions 
     3         /// </summary>
     4         /// <returns></returns>
     5         public string GetMailAddressByMatchData()
     6         {
     7             StringBuilder emailAddresses = new StringBuilder();
     8             nowDt = Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");//DateTime.Now;//
     9             using (SqlDataReader dr = SqlHelper.ExecuteReader(connectionStr, CommandType.Text, matchDataSqlStr))
    10             {
    11                 while (dr.Read())
    12                 {
    13                     string matchData = dr.GetString(dr.GetOrdinal("MatchData"));
    14                     if (IsMatchWithMatchData(matchData, nowDt))
    15                     {
    16                         string emails = dr.GetString(dr.GetOrdinal("Email"));
    17                         emailAddresses.Append(string.IsNullOrWhiteSpace(emails) ? string.Empty : ";" + emails);
    18                     }
    19                 }
    20             }
    21             return emailAddresses.Length > 0 ? emailAddresses.Remove(0, 1).ToString() : string.Empty;
    22         }
    23         /// <summary>
    24         /// judge whether the current time match all conditions in the xml string 'matchData'
    25         /// </summary>
    26         /// <param name="matchData"></param>
    27         /// <param name="nowDt"></param>
    28         /// <returns></returns>
    29         private bool IsMatchWithMatchData(string matchData, DateTime nowDt)
    30         {
    31             bool result = true;
    32             xmldoc.LoadXml(matchData);
    33             try
    34             {
    35                 XmlNodeList xmllist = xmldoc.SelectSingleNode("ScheduleDefinition").ChildNodes;
    36                 DateTime startDt = Convert.ToDateTime(xmllist[0].InnerText).ToLocalTime();
    37                 matchFunction.InitData(startDt, nowDt);
    38                 foreach (XmlNode xmlnode in xmllist)
    39                 {
    40                     MethodInfo method = matchType.GetMethod(xmlnode.Name + "Function", BindingFlags.Instance | BindingFlags.Public);
    41                     object oj = method.Invoke(matchFunction, new object[] { xmlnode.OuterXml });
    42                     if (result)
    43                         result = result && Convert.ToBoolean(oj);
    44                     else
    45                         break;
    46                 }
    47                 return result;
    48             }
    49             catch
    50             {
    51                 return false;
    52             }
    53         }
    复制代码
        GetMailAddressByMatchData方法是对应的 api接口他需要遍历从数据库里面得到的数据集,最后把符合条件的email地址返回给调用方。其中比较关键的是IsMatchWithMatchData方法用于判断xml配置条件和当前的时间nowDt的比较,如何前期时间在配置的时间条件内返回为真true否则为假false。遍历xml节点,反射到对应的方法,方法的名称是很有讲究的,是节点名称后面加一个后缀xmlnode.Name + "Function"。
    这样我们达到了业务的分离,每一个方法表示一个条件业务,需要扩展的时候,新增一种xml节点类型,然后新添加一个方法,其他的一概不用管,是不是很方便!
    有人说反射消耗性能,何不使用switch想简单工厂一样,遇到什么节点调用什么方法。其实我不太提倡这种方式:
    1:扩张性没有反射好,扩展的时候需要修改代码,不符合开闭原则;
    2:xml现在是两层条件,如果用switch,需要嵌套地使用switch了,如果以后变成三层的呢?所有灵活度也不如反射。
    3:其实现在反射的性能愈来愈好啦,几乎和一般的方式调用很接近了。我曾使用一万六千多条在3秒内完成,相信我们的员工没有这么多吧!也可以做一些其他的优化,例如利用缓冲,还有及时条件中只要一个条件为false,就直接返回为假,不用检测所有的XML里面的条件,相信你有更多的方式优化它。
    反射的方法类MatchWithMatchData里面:
     View Code
       比较有意思的是WhichWeekFunction方法,判断当前日期是哪一周,MonthlyDOWRecurrenceFunction等方法下面,还有条件,又使用了反射的方式调用。整体是如此简单,如此容易理解。测试的时候,把DateTime.Now;//Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");//改成Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");//DateTime.Now;//,这是一个小技巧。这个Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");测试用例下面有数据,总体跟踪一遍,就能理解这个代码
       最后祝大家好运,希望在能够帮助大家,也希望大家能多提意见,觉得有收获的帮助顶一下。
    代码下载:http://files.cnblogs.com/zhangxl/MatchData.zip
    文章出处:http://www.cnblogs.com/zhangxl/p/reflection.html 

     
    
    
    
    
     
     
     

     

     

     

     
    标签: 源码剖析Django

  • 相关阅读:
    JavaWeb
    申请百度开发者账号
    秋招C++面试相关总结索引
    游戏开发客户端
    Python源码剖析——02虚拟机
    Python源码剖析——01内建对象
    Pymongo 笔记
    调用其他文件__name__=='__main__'下代码
    Python 相关
    Python import本地模块
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3332409.html
Copyright © 2011-2022 走看看