ASP NET Web 表单 URL 重写太慢 网站速度太慢加载路线列表重构您的逻辑,以便您可以对其进行测试Chiave 逻辑

如何解决ASP NET Web 表单 URL 重写太慢 网站速度太慢加载路线列表重构您的逻辑,以便您可以对其进行测试Chiave 逻辑

我有一个需要 Url 重写规则的网站 (SITE_DOMAIN)。 该站点采用 asp.net Web 形式。

例如 SITE_DOMAIN/abbigliamento/donna/jeans 是 SITE_DOMAIN/Products/Donna/0/42/1

我有一个名为 Rewrites 的表,其中包含这些字段

IdRewrite    Chiave  Pagina  Param1  Param2  Param3  Param4  Param5  Type
2173    abbigliamento/donna/jeans   Products    Donna   0   42  1   NULL    Categorie

在 Global.asax 我有

   public static List<Rewrite> rewrites = null;
   public static string oldChiave = "";

   public void GetRewrites() 
   {
      if (rewrites == null)
         rewrites = Rewrite.getRules(); //reads from table (about 5000 rows)
   }


protected void Application_BeginRequest(object sender,EventArgs e)
   {
      
      GetRewrites();
      String fullOriginalPath = Request.Url.ToString();
      int index = fullOriginalPath.IndexOf('/',fullOriginalPath.IndexOf(SITE_DOMAIN)) + 1;
      string chiave = fullOriginalPath.Substring(index).ToLower();

      if (oldChiave != chiave)
      {
         oldChiave = chiave;            

         Rewrite r = rewrites.Find(y => y.Chiave == chiave);

         if (r != null)
         {
            string url = "/" + r.Pagina;
            if (r.Param1 != null)
                url += "/" + r.Param1;
            if (r.Param2 != null)
                url += "/" + r.Param2;
            if (r.Param3 != null)
                url += "/" + r.Param3;
            if (r.Param4 != null)
                url += "/" + r.Param4;
            if (r.Param5 != null)
                url += "/" + r.Param5;
            Context.RewritePath(url);
         }

         //se non ho trovato la chiave all'interno delle chiavi potrebbe essere la composizione dei parametri in Param1,2,3,4,5 es /Products/Uomo/0/0/1,deve ritrasformarsi in Scarpe-Uomo
         string[] param = chiave.Split('/');
         if (param.Length == 5)
         {
            r = rewrites.Find(x => x.Pagina == param[0] &&
                    x.Param1 == param[1] &&
                    x.Param2 == param[2] &&
                    x.Param3 == param[3] &&
                    x.Param4 == param[4]);

            if (r != null)
                Response.Redirect("/" + r.Chiave);

         }
         if (param.Length == 6)
         {
            r = rewrites.Find(x => x.Pagina == param[0] &&
                    x.Param1 == param[1] &&
                    x.Param2 == param[2] &&
                    x.Param3 == param[3] &&
                    x.Param4 == param[4] &&
                    x.Param5 == param[5]);

            if (r != null)
                Response.Redirect("/" + r.Chiave);
         }
      }

   }

网站速度太慢,我确定问题就在这里。

当我点击一个链接时,Global.asax Application_BeginRequest 方法会被触发 3 次或更多次。

是否有任何其他方法可以使用或任何 3 部分 dll 可以使用?

附注。为了我的 global.asax 的完整性,我也有方法

    using Microsoft.AspNet.FriendlyUrls;
    protected void Application_Start(object sender,EventArgs e)
   {
      RouteTable.Routes.EnableFriendlyUrls();
      RouteTable.Routes.MapPageRoute("","home","~/Default.aspx");
      RouteTable.Routes.MapPageRoute("","carrello","~/Carrello.aspx");
      RouteTable.Routes.MapPageRoute("","contatti","~/Contatti.aspx");
      RouteTable.Routes.MapPageRoute("","checkout","~/Checkout2.aspx");
      RouteTable.Routes.MapPageRoute("","logout","~/Logout.aspx");
      RouteTable.Routes.MapPageRoute("","pagamenti","~/Pagamenti.aspx");
      RouteTable.Routes.MapPageRoute("","chi-siamo/scarpe-online-di-marca","~/ChiSiamo.aspx");
      RouteTable.Routes.MapPageRoute("","i-miei-ordini","~/PageOrdini.aspx");
      RouteTable.Routes.MapPageRoute("","pre-checkout","~/PreCheckout.aspx");
      RouteTable.Routes.MapPageRoute("","privacy-and-cookies","~/PrivacyAndCookies.aspx");
      RouteTable.Routes.MapPageRoute("","ricerca-prodotto/{Filtri}/{Pagina}","~/ProductsSearch.aspx");
      RouteTable.Routes.MapPageRoute("","il-mio-profilo","~/Profilo.aspx");
      RouteTable.Routes.MapPageRoute("","registrazione","~/Registrazione.aspx");
      RouteTable.Routes.MapPageRoute("","resi","~/Resi.aspx");
      RouteTable.Routes.MapPageRoute("","spedizioni","~/Spedizioni.aspx");
      RouteTable.Routes.MapPageRoute("","termini-e-condizioni","~/TerminiECondizioni.aspx");
      RouteTable.Routes.MapPageRoute("","grazie","~/Thanks.aspx");
      RouteTable.Routes.MapPageRoute("","product/{ProductId}","~/Product.aspx");
      RouteTable.Routes.MapPageRoute("","products/{Menu}/{Marca}/{Categoria}/{Pagina}","~/Products.aspx");
      RouteTable.Routes.MapPageRoute("","blog/{Pagina}","~/Blog.aspx");
      RouteTable.Routes.MapPageRoute("","lista-dei-desideri/{Pagina}","~/Wishlist.aspx");
      RouteTable.Routes.MapPageRoute("","blogpost/{NewsId}","~/BlogPost.aspx");

   }

这个重写是固定的,所以它们不需要存储在以前的表中。

解决方法

由于您对第三方解决方案持开放态度(“我可以使用任何其他方法或我可以使用任何由 3 部分组成的 dll?”),您可以考虑以下内容。

  1. 选项 1:由于您的网站是使用 Microsoft 技术堆栈构建的,我假设您将在 IIS 网络服务器上部署该解决方案。如果是这种情况,您可以使用 IIS 上的重写模块扩展来完成所有重写。您的规则将保存在 web.config 文件中。
  2. 选项 2:使用第三方软件

选项 1 步骤

  • 打开 IIS 管理器。选择您的网站。
  • 单击功能视图中的 URL 重写。如果您没有看到此选项,则可能尚未安装 URL 重写扩展。从这里获取它 [https://www.iis.net/downloads/microsoft/url-rewrite] 并安装它。
  • 单击“操作”窗格中的“添加规则”(位于右侧)
  • 在添加规则对话框中选择空白规则
  • 创建您的规则。规则选项一目了然。

由于您的规则存储在数据库中,您可能需要编写代码来从数据库中读取您的规则(您似乎已经这样做了)并重新格式化规则以确认为 IIS 规则格式。归根结底,规则是名称-值对。假设您有一个 prod.aspx 页面,该页面采用显示产品详细信息的产品 ID 和尺寸参数,并且您当前的 URL 是“/products/prod.aspx?id=1234&size=3”。在 IIS 规则映射对话框中,规则映射中的“原始值”将是“/product-details”,而“新值”将是“/products/prod.aspx?id=1234&size=3”。原始值指定我们要重写的 URL 路径,新值指定我们要重写的 URL 路径。

您可能需要使用带有 URL 重写模块的自定义重写提供程序直接与 SQL 表交互以导入规则。您可以在此处阅读有关如何执行此操作的更多信息:https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/using-custom-rewrite-providers-with-url-rewrite-module

选项 2

我自己没有使用过上述任何软件产品,因此,我无法告诉您它们的工作情况。风险自负。

,

正如其他答案所建议的那样,有不同的 MiddleWare 选项可用或 Url-Rewrite 模块可以应用于您正在运行的应用程序之外,但是如果您不了解实际的瓶颈在哪里,您如何确定任何这方面的改变会有效吗?

这里有多个复合问题。

  1. 网站速度太慢

    您对太慢的定义是什么?您希望达到什么响应时间?

    • 虽然复杂的路由逻辑看起来像一个主要候选者,但即使它看起来效率低下,最坏的情况也应该只为每个响应增加几百毫秒。
    • 知道每个页面请求(取决于其中的架构和代码)通常会发出多个请求,有时是 10 或 100 个单独的内容请求,问题可能不是路由,而是服务的问题每个方面的内容,或者可能只是瓶颈所在的一些请求。
    • 使用浏览器中的开发工具来确定页面生命周期中哪些单个请求执行时间最长,缓慢响应可能不会 是一个重要因素。
  2. 加载路线列表

    虽然我们假设的数据库调用被加载到静态变量中,但代码模式是不明确的,而不是“根据请求”执行这个,它应该被强制在 Application_Start 中进行一次调用。但是不要忽视确保此调用有效的重要性,如果这个简单的数据库查找需要很长时间才能响应,那么我们可以安全地假设对数据库的所有其他查询也会很慢,这是最有可能的响应时间的因素

    • 5K 记录听起来并不多,但在某些框架和低成本架构中,它可能会使冷启动时间增加几秒钟,在这种情况下,您应该考虑读取数据的编译或部署步骤从数据库到本地文件或使用 T4 模板或类似的东西从规则生成代码。

    • 您需要知道执行 Rewrite.getRules() 需要多长时间才能在这一点上做出决定,像这样的简单代码将有助于捕获获取规则所花费的时间,如果这是不可接受的,那么这是一个明确的优化位置,我希望 5k 行在生产环境中仍能在 1 秒内返回:

      public static List<Rewrite> rewrites = null;
      public static TimeSpan _getRules_Duration = TimeSpan.Zero;
      
      public void GetRewrites() 
      {
         if (rewrites == null)
         {
             var sw = System.Diagnostics.Stopwatch.StartNew();
             try
             {
                 rewrites = Rewrite.getRules(); //reads from table (about 5000 rows)
             }
             finally
             {
                 sw.Stop();
                 _getRules_Duration = sw.Elapsed;
             }
          }
      }
      

      发布 _getRules_Duration 的值,如果超过 1 秒,则表明您的数据访问实现存在严重问题。如果像这样的简单查询花费的时间太长,那么您的整个数据驱动的网站从一开始就注定要失败,请看回以上几点,如果一个页面对数据库进行了多次单独的点击,那么每个查询也可能受到影响表现不佳的 DAL

      即使是 1 秒也很慷慨,我只是分析了一个更复杂的 EF 查询,返回超过 10K 行,包含 4 个级别的包含关系数据,并且在 100 毫秒内全部返回,整个页面响应更接近于700 毫秒,所以即使我要优化数据库查询,最好的情况是整个页面的加载时间回到接近 600 毫秒,那么为这么小的改进付出努力是否值得?

      糟糕的 DAL 可能是由糟糕的代码或糟糕的 ORM 逻辑实现造成的,但是我们经常忽视部署网站运行的操作环境和资源。如果您无法提高带宽或资源,Web 客户端、Web 服务器、DAL 和数据库之间的延迟会给您的代码带来关键的物理硬件瓶颈。

    将加载路由的调用移动到 Application_Start 中,以明确此数据在应用程序生命周期中仅加载一次,但每个请求都需要它,因此您想尽早知道它是否会进行失败:

    protected void Application_Start(object sender,EventArgs e)
    {
        GetRewrites();
    
        RouteTable.Routes.EnableFriendlyUrls();
        ...
    }
    
  3. 重构您的逻辑,以便您可以对其进行测试

    现在,OP 和所有其他帖子都专注于用于匹配路由的逻辑,但与加载时间类似,如果路由逻辑只需要 200 毫秒来评估,那么我们最多只能减少每个单独的响应时间数量。让我们将此逻辑重构为我们既可以测试又可以衡量的格式:

    private string oldChiave;
    protected void Application_BeginRequest(object sender,EventArgs e)
    {
        String fullOriginalPath = Request.Url.ToString();
        int index = fullOriginalPath.IndexOf('/',fullOriginalPath.IndexOf(SITE_DOMAIN)) + 1;
        string chiave = fullOriginalPath.Substring(index).ToLower();
    
        if (oldChiave != chiave)
        {
            oldChiave = chiave;
            if (TryGetRewritePath(chiave,out string rewriteUrl))
                Context.RewritePath(rewriteUrl);
            else if (TryGetRewritePath(chiave,out string redirectUrl))
                Context.Redirect(redirectUrl);
        }
    }
    
    public bool TryGetRewritePath(string chiave,out string url)
    {
        Rewrite r = rewrites.Find(y => y.Chiave == chiave);
    
        if (r != null)
        {
            string url = "/" + r.Pagina;
            if (r.Param1 != null)
                url += "/" + r.Param1;
            if (r.Param2 != null)
                url += "/" + r.Param2;
            if (r.Param3 != null)
                url += "/" + r.Param3;
            if (r.Param4 != null)
                url += "/" + r.Param4;
            if (r.Param5 != null)
                url += "/" + r.Param5;
            return true;
        }
        return false;
    }
    
    public bool TryGetRedirectPath(string chiave,out string url)
    {
        //se non ho trovato la chiave all'interno delle chiavi potrebbe essere la composizione dei parametri in Param1,2,3,4,5 es /Products/Uomo/0/0/1,deve ritrasformarsi in Scarpe-Uomo
        string[] param = chiave.Split('/');
        if (param.Length == 5)
        {
            Rewrite r = rewrites.Find(x => x.Pagina == param[0] &&
                    x.Param1 == param[1] &&
                    x.Param2 == param[2] &&
                    x.Param3 == param[3] &&
                    x.Param4 == param[4]);
    
            if (r != null)
            {
                url = "/" + r.Chiave;
                return true;
            }
        }
        if (param.Length == 6)
        {
            Rewrite r = rewrites.Find(x => x.Pagina == param[0] &&
                    x.Param1 == param[1] &&
                    x.Param2 == param[2] &&
                    x.Param3 == param[3] &&
                    x.Param4 == param[4] &&
                    x.Param5 == param[5]);
    
            if (r != null)
            {
                url = "/" + r.Chiave;
                return true;
            }
        }
    
        return false;
    }
    

    现在,与 GetRewrites 示例一样,我们可以再次使用秒表来记录每个请求的持续时间,这里我们将仅跟踪信息,您可以根据自己的需要进行调整,也许可以存储最大或平均处理时间

    总体而言,您需要数据来通知您这是否是您整体性能问题的根源。

     protected void Application_BeginRequest(object sender,EventArgs e)
     {
         String fullOriginalPath = Request.Url.ToString();
         int index = fullOriginalPath.IndexOf('/',fullOriginalPath.IndexOf(SITE_DOMAIN)) + 1;
         string chiave = fullOriginalPath.Substring(index).ToLower();
    
         if (oldChiave != chiave)
         {
             oldChiave = chiave;
    
             var sw = System.Diagnostics.Stopwatch.StartNew();
             if (TryGetRewritePath(chiave,out string rewriteUrl))
             {
                 sw.Stop();
                 // log out the duration
                 System.Diagnostics.Trace.WriteLine($"URL Rewrite evaluated in: {sw.ElapsedMilliseconds}ms. '{chiave}' => '{rewriteUrl}'");
                 Context.RewritePath(rewriteUrl);
             }
             else if (TryGetRewritePath(chiave,out string redirectUrl))
             {
                 sw.Stop();
                 // log out the duration (this includes the above dureation AS WELL)
                 System.Diagnostics.Trace.WriteLine($"URL Redirect evaluated in: {sw.ElapsedMilliseconds}ms. '{chiave}' => '{rewriteUrl}'");
                 Context.Redirect(redirectUrl);
             }
             else
             {
                 sw.Stop();
                 // log out the duration (this includes the above dureation AS WELL)
                 System.Diagnostics.Trace.WriteLine($"NO REDIRECT evaluated in: {sw.ElapsedMilliseconds}ms. '{chiave}'");
             }
         }
     }
    

    OP 请发布您的代码完成这些功能所需的时间,您可以从中决定路由处理是否是重要因素。

  4. Chiave 逻辑

    您代码中的另一个明显问题是,您试图阻止根据 Chiave 是否已更改对路由逻辑进行多次评估。如果您的站点处理对路径前缀的多个值的请求,那么我不确定此逻辑是否完全正确。如果在不同业务 Chiave 身份下运营的两个不同用户同时使用您的网站,那么每个用户都会导致 oldChiave 的先前值被覆盖并执行相同的逻辑。

    作为最小步骤,请确保 oldChiaveinstance 成员,而不是 static 成员。但我不确定这是否真的对您的问题有帮助,您可能想要实现的是以下类型的逻辑:

    On Request:
       - If the current URL has already been evaluated for redirect,use the previous result
       - Otherwise,check if we need to redirect,and if we do,cache the result for next time.
    

    像往常一样,有很多不同的代码模式可以解决这个问题,但是首先要知道这是否会改善您的响应时间,如果是,那么会提高多少.这只能通过逻辑分析来确定。

注意这里使用了static,我们不知道哪个应用程序实例可能正在为请求提供服务。

   static Dictionary<string,Tuple<string,string>> rewriteCache = new Dictionary<string,string>>(); 
   protected void Application_BeginRequest(object sender,EventArgs e)
   {
       String fullOriginalPath = Request.Url.ToString();
       int index = fullOriginalPath.IndexOf('/',fullOriginalPath.IndexOf(SITE_DOMAIN)) + 1;
       string chiave = fullOriginalPath.Substring(index).ToLower();

       if (rewriteCache.TryGetValue(chiave,out Tuple<string,string> cacheItem))
       {
           if(cacheItem != null)
           {
               if(cacheItem.Item1 != null) Context.RewritePath(cacheItem.Item1);
               if(cacheItem.Item2 != null) Context.Redirect(cacheItem.Item2);
           }
       }
       else
       { 
           if (TryGetRewritePath(chiave,out string rewriteUrl))
           {
               rewriteCache.Add(chiave,new Tuple<string,string>(rewriteUrl,null));
               Context.RewritePath(rewriteUrl);
           }
           else if (TryGetRewritePath(chiave,out string redirectUrl))
           {
               rewriteCache.Add(chiave,string>(null,redirectUrl));
               Context.Redirect(redirectUrl);
           }
           else
           {
               // Cache the no redirect scenario
               rewriteCache.Add(chiave,null);
           }
       }
   }
,

Application_BeginRequest 方法在我单击时触发 3 次或更多次 一个链接

为了尽量减少调用 - Application_BeginRequest 会针对所有请求调用,而不仅仅是针对 aspx 文件。

您只能最小化 aspx 和处理程序文件。下面是一个关于如何做到这一点的示例:

    string sExtentionOfThisFile = System.IO.Path.GetExtension(HttpContext.Current.Request.Path);
    
if ( sExtentionOfThisFile.Equals(".aspx",StringComparison.InvariantCultureIgnoreCase) ||
     sExtentionOfThisFile.Equals(".ashx",StringComparison.InvariantCultureIgnoreCase)
)
{
    // run here your Rewrites
}

rewrites = Rewrite.getRules(); //从表中读取(大约 5000 行)

重写 r = rewrites.Find(y => y.Chiave == chiave);

这是您对每次调用进行 5000 次搜索循环(可能平均为 2500 次)的点。如果访问量最大的页面位于列表末尾,则进行成像,那么每次调用时都会有近 5000 个比较字符串 - 这就是您遇到的主要问题。

为了在您的特定情况下更快,我建议使用 Dictionary<> - 需要更多的代码和不同的搜索方式 - 但会有所不同。

还使用不同的搜索方式优化该代码 - 这种方式使用 Dictionary<>

        r = rewrites.Find(x => x.Pagina == param[0] &&
                x.Param1 == param[1] &&
                x.Param2 == param[2] &&
                x.Param3 == param[3] &&
                x.Param4 == param[4]);
,

我会添加 Aristos 所说的扩展验证,我也可能会搜索“查找”实现(使用二叉树或其他方法)以使其更快。

我认为这就是问题所在。

对数据库的查询不是问题,因为它只运行一次。

另一种可能的解决方案是在速度更快的 redis 服务器中设置列表(尽管您需要运行一些测试来比较 redis 与 5000 项或更多项的内存以使其面向未来)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-