分享Web应用运行的细节问题:预编译提高网站性能、跟踪用户习惯和解决线程同步...

在这个文章里,我将分享一下在iOpenWorks.com这个网站试运行中碰到的若干问题和解决方案,这些问题包含了:(1)如何通过ASP.NET MVC预编译提高性能;(2)如何知道网站在运行中,用户响应速度、网站异常信息、用户操作习惯;(3)解决与DiscuzToolkit集成的线程同步问题。

 

1 ASP.NET MVC 3预编译支持

提高网站性能,除了我们常见的压缩、CDN、缓存之外,还有一个就是使用预编译。不管是ASP.NET WebForm,或者是ASP.NET MVC,这些页面在网站运行过程中,都是要先经过编译处理的。因此,如果能在网站运行前对其进行编译,那无疑能更好的提高网站的响应速度。因此,我们选择了一个RazorGenerator来对所有的ASP.NET MVC 3的视图进行编译,这样,在部署时仅需要将dll文件拷贝过去,而不再需要cshtml文件了。下面介绍如何使用它来实现预编译。

1.1 下载安装RazorGenerator

你可以在http://razorgenerator.codeplex.com/下载到RazorGenerator,这是一个VS 2010的扩展。下载完成后,就可以直接安装了。接着你还需要下载源代码,然后编译一下,获取编译的RazorGenerator.Mvc.dll程序集。

 

1.2 改变视图文件的生成方式

将所有的视图的BuildAction改成None,并且将CustomTool改成RazorGenerator,这时候,你可以看到一个关联的.generated.cs文件,这个文件就是预编译的源码文件了。

image

1.3 处理Helper

对于Helpr文件,处理方式有所不同。Helper文件一般放在App_Code文件夹里面。首先,你需要在Helper文件的第一行添加 @* Generator: MvcHelper *@ 来声明一下,接着将BuildAction改成None,并且将CustomTool改成RazorGenerator

image

下面,还需要额外一个步骤,这个非常重要,否则编译无法通过,那就是需要将.generated.cs文件的BuildAction由Content改为Compile。

image

 

1.4 注册PrecompiledMvcEngine

下面我们在ASP.NET MVC 3项目中引用RazorGenerator.Mvc.dll这个程序集,然后定义一个PreApplicationStartCode,并在AssemblyInfo.cs文件中注册这个PreApplicationStartCode。这样,我们就注册了PrecompiledMvcEngine了。

(1)在AssemblyInfo.cs注册

 

[assembly: PreApplicationStartMethod( 
    typeof(UIShell.iOpenWorks.PreApplicationStartCode), "PreStart")]

 

(2)PreApplicationStartCode定义

 

复制代码
namespace UIShell.iOpenWorks 

    public class PreApplicationStartCode 
    { 
        private static bool _isStarting;
        public static void PreStart() 
        { 
            if (!_isStarting) 
            { 
                _isStarting = true;
                var engine = new PrecompiledMvcEngine( 
                    typeof(PreApplicationStartCode).Assembly);
                ViewEngines.Engines.Add(engine);
                VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 
            } 
        } 
    } 
}
复制代码

 

 

1.5 部署

这时候,部署网站就不再需要将视图文件部署过去了,只需要拷贝dll文件和网站资源。注意,在Views下面已经没有.cshtml文件了,也没有App_Code文件,因为它们都被预编译到了UIShell.iOpenWorks.dll这个程序集了。接下来,你就可以测试一下网站,享受一下预编译带来的性能提升了。

imageimage

 

2 跟踪网站运行情况

网站在内测期间,会碰到较多的问题。但是,这时候,用户已经进来测试,你怎么能够及时发现用户响应速度、用户访问过程中网站异常信息以及用户是如何来使用你的网站。这里,我们使用了log4net这个日志组件,它用于记录:(1)用户访问了哪些页面;(2)用户在访问页面过程中,碰到了哪些异常;(3)每一个页面的响应速度。下面,我来介绍如何记录这些信息的。

2.1 在Global中,跟踪每个用户访问的页面,并且要记录用户响应的速度

 

复制代码
[ThreadStatic] 
private static Stopwatch _stopwatch;
protected void Application_BeginRequest() 

    _stopwatch = Stopwatch.StartNew();  // 计时开始 
    if (DiscuzHelper.IsLoggedIn()) // 记录当前用户 
    { 
        try 
        { 
            var user = DiscuzHelper.LoggedUser(); 
            if(user != null
            { 
                ThreadContext.Properties["user"] = user.UserName; 
                return
            } 
        } 
        catch (Exception ex) 
        { 
            _logger.Error("Failed to get the user name though the user is logged in.", ex); 
        } 
    } 
            
    ThreadContext.Properties["user"] = string.Empty;
    if (Request != null// 记录当前用户的IP 
    { 
        ThreadContext.Properties["ipaddress"] = Request.ServerVariables["REMOTE_ADDR"]; 
    } 
    else 
    { 
        ThreadContext.Properties["ipaddress"] = string.Empty; 
    } 
}
protected void Application_EndRequest() 

    if (Request != null && _stopwatch != null && _logger != null)  // 计时结束,就用户响应时间和访问页面 
    { 
        _stopwatch.Stop(); 
        _logger.Debug(string.Format("Accessed page 'Response time: {0} ms, Url: {1}'.", _stopwatch.ElapsedMilliseconds, Request.Url)); 
    } 
}
复制代码

 

2.2 在Global中,记录系统的异常

 

复制代码
void Application_Error(Object sender, EventArgs ea) 

    if (Server != null
    { 
        Exception e; 
        for (e = Server.GetLastError(); e != null; e = e.InnerException) 
        { 
            _logger.Error("Unhandled server exception thrown.", e); 
        } 
    } 
}
复制代码

 

 

2.3 处理关键方法

下面,我还在关键方法记录了用户的操作异常信息、响应速度。比如我必须记录了:(1)用户注册时响应速度、注册时发生的异常、用户登录时响应速度、用户登录时发生的异常;(2)用户在什么情况下尝试下载iOpenWorksSDK这个免费插件框架;(3)尝试下载时,会转到注册页面,这时候用户是否继续注册并下载,还是放弃。

对这些关键方法的记录,有助于提高应用系统的易用性。通过日志,我们修复了与Discuz集成的很多问题,并且提高了用户响应速度。

 

2.4 日志分析

下面,我们需要来看一下日志分析,这里我们在一个开源的LogViewer自定义了一下。通过对日志的分析,你就可以知道系统发生了什么异常、系统性能如何、用户操作习惯、关键方法的信息。当然,你也可以打开日志文件直接查看,只是,那样比较费劲。对了,在这里我们绝不记录用户的密码,这太不职业道德了,此外,所有密码都是加密的,避免“CSDN”!

(1)查看异常信息

image

(2)查看关键方法信息:用户访问习惯、响应性能等

image

 

3 解决DiscuzToolkit线程同步

网站的社区是与Discuz集成的,我们就用了DiscuzToolkit来集成。这是官方发布的类库,但是依然问题一堆。最严重的2个问题就是线程同步引起的,可见Discuz这帮人对ASP.NET多线程模型压根没有当一回事,或者连线程安全都没有注意到。下面就说一下碰到的2个线程安全问题。

(1)在注册用户时,碰到以下异常:当前会话所提交的call_id没有大于前一次的call_id

Failed to get the user name though the user is logged in.
Discuz.Toolkit.DiscuzException: Code: 103, Message: 当前会话所提交的call_id没有大于前一次的call_id
at Discuz.Toolkit.Util.GetResponse[T](String method_name, DiscuzParam[] parameters) in E:\Work\Design\Core\milestore 1\osgi\m10\uishell.iopenworks\DiscuzToolkit\Util.cs:line 97
at Discuz.Toolkit.DiscuzSession.GetUserInfo(Int64[] uids, String[] fields) in E:\Work\Design\Core\milestore 1\osgi\m10\uishell.iopenworks\DiscuzToolkit\DiscuzSession.cs:line 224
at Discuz.Toolkit.DiscuzSession.GetUserInfo(Int64 uid) in E:\Work\Design\Core\milestore 1\osgi\m10\uishell.iopenworks\DiscuzToolkit\DiscuzSession.cs:line 255

这个问题是由Discuz.Toolkit.Util的Sign方法引起的,在这里,它为每一个API请求生成一个call_id。

 

list.Add(DiscuzParam.Create("call_id", DateTime.Now.Ticks));

 

 

如果你在当前线程API调用太勤快的话,DateTime.Now.Ticks会生成一样的值,从而引发异常。因此,官方提议可以Sleep一下。因此,我们就需要改成如下:

 

list.Add(DiscuzParam.Create("call_id", DateTime.Now.Ticks)); 
// Avoid to generate same 'call_id' and throws an exception on '当前会话所提交的call_id没有大于前一次的call_id'. 
Thread.Sleep(50);

 

 

但是这样,依然是不行的,这个异常只是变得更加诡异了,让你碰到机会少一点而已。你别忘了ASP.NET应用程序是多线程的,当两个线程同时访问时,依然可能获得同一个call_id,于是,在碰到若干次这个问题后,我用以下方法来修复。

 

lock (_syncRoot) 

    list.Add(DiscuzParam.Create("call_id", DateTime.Now.Ticks)); 
    // Avoid to generate same 'call_id' and throws an exception on '当前会话所提交的call_id没有大于前一次的call_id'. 
    Thread.Sleep(50); 
}

 

 

(2)注册用户时,碰到以下异常:An item with the same key has already been added.

[2012-04-07 17:11:30,818] [7] [ERROR] [AccountController] [49.72.46.135] []: System.ArgumentException: An item with the same key has already been added.
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
at Discuz.Toolkit.Util.GetSerializer(Type t) in E:\Work\Design\Core\milestore 1\osgi\m10\uishell.iopenworks\DiscuzToolkit\Util.cs:line 157
at Discuz.Toolkit.Util.GetResponse[T](String method_name, DiscuzParam[] parameters) in E:\Work\Design\Core\milestore 1\osgi\m10\uishell.iopenworks\DiscuzToolkit\Util.cs:line 88
at Discuz.Toolkit.DiscuzSession.GetUserID(String username) in E:\Work\Design\Core\milestore 1\osgi\m10\uishell.iopenworks\DiscuzToolkit\DiscuzSession.cs:line 243
at UIShell.iOpenWorks.Controllers.AccountController.Register(DiscuzNewUser newUser, String returnUrl) in E:\Work\Design\Core\milestore 1\osgi\m10\uishell.iopenworks\UIShell.iOpenWorks\Controllers\AccountController.cs:line 53

 

你想想,要是用户注册时,动不动碰到注册不成功,是多么窝火!!所以,我根据日志再次调查发现,DiscuzToolkit在使用静态变量保存数据时,竟然不加锁,太不拿Thread-Safe当回事了。这会异常也发生在Util类里,代码如下,其中serializer_dict是静态全局变量。

 

serializer_dict.Add(type_hash, new XmlSerializer(t));

 

于是,我修改如下。这样,彻底解决了和Discuz的集成了。

 

复制代码
private static Dictionary<int, XmlSerializer> serializer_dict = new Dictionary<int, XmlSerializer>(); 
private static ReaderWriterLock _lock = new ReaderWriterLock(); 
public static XmlSerializer GetSerializer(Type t) 

    int type_hash = t.GetHashCode(); 
    const int timeout = 5000;
    try 
    { 
        _lock.AcquireReaderLock(timeout); 
        if (!serializer_dict.ContainsKey(type_hash)) 
        { 
            _lock.UpgradeToWriterLock(timeout); 
            if (!serializer_dict.ContainsKey(type_hash)) 
            { 
                serializer_dict.Add(type_hash, new XmlSerializer(t)); 
            } 
        }
        return serializer_dict[type_hash]; 
    } 
    catch (ApplicationException ex) 
    { 
        throw new Exception("Accquire lock failed.", ex); 
    } 
    finally 
    { 
        if (_lock.IsReaderLockHeld) 
        { 
            _lock.ReleaseReaderLock(); 
        } 
        else if (_lock.IsWriterLockHeld) 
        { 
            _lock.ReleaseWriterLock(); 
        } 
    } 
}
复制代码

 

 

OK,关于网站试运行中,最重要的几点分享描述完了。顺道介绍一下什么是iOpenWorks.com。iOpenWorks.com是一个免费开放的插件仓库,旨在向开发人员提供完全免费的标准化的OSGi.NET面向服务插件框架以及共享的插件仓库,这样,你既可以从插件仓库使用别人插件,也可以共享自己的插件,互利共赢!

 

你也可以加入iOpenWorks交流群:121369588,Thanks。


本文转自道法自然博客园博客,原文链接:http://www.cnblogs.com/baihmpgy/archive/2012/04/09/2438720.html,如需转载请自行联系原作者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283088.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mondrain配置mysql_mondrian 4.7 源码部署(示例代码)

mondrian是一个开源的数据分析工程, 网上有关mondrian3.X的源码部署比较多, 有关4.X的部署较少. 目前官方推荐使用的时mondrian3.7的修订版, 可以再github上下载到最近更新维护的mondrian-master, 下载下来后基本上只需要按部就班的使用maven build一下就可以正常使用了, 如有问…

腾讯云DevOps技术揭秘:新时代运维重器Tencent Hub最佳实践

随着云计算和容器技术的发展以及微服务架构的兴起&#xff0c;服务能够实现细粒度的部署&#xff0c;维护和伸缩。在使开发人员能快速开发的同时&#xff0c;这些技术也给系统和应用的运维带来了更大的挑战。DevOps理念也应运而生&#xff0c;强调研发和运维的流程及工具的自动…

.Net CoreRabbitMQ消息存储可靠机制(下)

前言上篇讨论过消息投递和消息消费过程中如何确保可靠传输&#xff0c;也提及到消息到达RabbitMQ中到被消费前也需要可靠的留存&#xff0c;可因许多的不确定因素会影响着消息的存在与否。消息中转点生产者发送消息到RabbitMQ中&#xff0c;如果交换机根据自身类型和RoutingKey…

nginx安装及负载均衡配置

Nginx (“engine x”) 是一个高性能的 HTTP 和 反向代理 服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。 Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的&#xff0c;第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发…

智能停车O2O 独角兽初现:“ETCP停车”获5000万美金A轮融资

日前&#xff0c;国内第一智能停车平台“ETCP停车”宣布完毕A轮融资&#xff0c;由源代码资本、SIG、易车网、经纬中国和商企界知名人士联合投资超过5000万美金。同一时候获悉&#xff0c;ETCP作为中国智能停车行业龙头老大&#xff0c;不仅是该行业的创建者和领军品牌&#xf…

ASP.NET MVC使用Bootstrap系统(2)——使用Bootstrap CSS和HTML元素

阅读目录 Bootstrap 栅格&#xff08;Grid&#xff09;系统Bootstrap HTML元素Bootstrap 验证样式ASP.NET MVC创建包含Bootstrap样式编辑模板小结Bootstrap提供了一套丰富CSS设置、HTML元素以及高级的栅格系统来帮助开发人员快速布局网页。所有的CSS样式和HTML元素与移动设备优…

VS2017调试闪退之Chrome

原文:VS2017调试闪退之Chrome巨硬build后发了15.7.1满载期待的升级了。。结果NM泪奔................... 为啥 泪奔&#xff1f; 使用Chrome 调试闪退&#xff0c;MMP ,一想肯定是VS的锅咯&#xff0c;各种抓头发。。 试试看看VS配置发现 &#xff0c;多了点东西。。 都勾上后&…

ios UISearchBar搜索框的基本使用

摘要: 小巧简洁的原生搜索框&#xff0c;漂亮而易用&#xff0c;如果我们的应用没有特殊需求&#xff0c;都可以使用它。iOS中UISearchBar(搜索框)使用总结 初始化&#xff1a;UISearchBar继承于UIView&#xff0c;我们可以像创建View那样创建searchBar UISearchBar * bar [[U…

Win8下怎样安装Win7 or Win7下怎样安装win8?

预计非常多人可能会用U盘安装工具去去做双系统的安装&#xff08;Win8下安装Win7&#xff0c; Win7下安装Win8&#xff09;。可是在安装过程中你 会发现一个问题&#xff1a;win7下安装win8&#xff0c;提示你mbr硬盘格式不能安装win8&#xff1b;win8下安装win7&#xff0c;提…

Linux 练习题-3文件与磁盘 问答

1、描述Liux下软链接和硬链接的区别创建命令不同&#xff0c;ln 命令创建硬链接&#xff0c;ln -s 创建软链接inode节点号不同&#xff0c;硬链接inode与源文件相同&#xff0c;软链接inode与源文件不同使用对象不同&#xff0c;硬链接只能对文件使用&#xff0c;软链接可以对文…

.NET Offer 快到碗里来!.NET 招聘季

关注我们谈到 .NET 在中国的推广和发展&#xff0c;.NET 开发者求职就业及 .NET 企业招人用人的问题往往常被提及。初学者会担心学习 .NET 之后的就业问题&#xff0c;.NET 开发者在求职过程中没有足够多的渠道来获取 .NET 招聘信息&#xff0c;而与此同时&#xff0c;采用 .NE…

java的collections_Java中Collection和Collections的区别

1) 排序(Sort)使用sort方法可以根据元素的自然顺序 对指定列表按升序进行排序。列表中的所有元素都必须实现 Comparable 接口。此列表内的所有元素都必须是使用指定比较器可相互比较的1 List list new ArrayList();2 int array[] {112, 111, 23, 456, 231};3 for (int i 0; …

jQuery事件绑定(一)

2019独角兽企业重金招聘Python工程师标准>>> 一、on方法 在Jquery1.7中添加&#xff0c;用来代替其他事件绑定方法。向匹配元素添加一个或多个事件处理程序 使用语法&#xff1a; $(selector).on(event,childselector,data,function) 参数&#xff1a; event&#x…

JDBC学习笔记之JDBC简介

1. 引言 JDBC API是一种Java API&#xff0c;可以访问任何类型的表格数据&#xff0c;特别是存储在关系数据库中的数据。 JDBC可以帮助我们编写下列三种编程活动的java应用程序&#xff1a; 1.连接到数据源&#xff0c;如数据库;2.发送查询和更新语句到数据库;3.检索并处理从数…

PaddleOCR在 windows下的webAPI部署方案

很多小伙伴在使用OCR时都希望能过采用API的方式调用&#xff0c;这样就可以跨端跨平台了。本文将介绍一种基于python的PaddleOCR识方案。喜欢的可以关注公众号&#xff0c;获取更多内容。# 一、 windows环境下部署###1.环境操作系统&#xff1a;windows10&#xff1b;主要软件环…

Vim的NerdTree插件

一个项目文件多起来时&#xff0c;左边的文件树菜单是必要的。参考&#xff1a;常用文件树快捷键所有命令及推荐键盘映射&#xff1a;官方 在vundle插件管理的方式&#xff0c;直接在~/.vimrc中的Plugin段落中加入Plugin "scrooloose/nerdtree"然后重启Vim并输入Plug…

java简单投票系统_JSP实现的简单Web投票程序代码

本文实例讲述了JSP实现的简单Web投票程序。分享给大家供大家参考。具体如下&#xff1a;这里使用文本文件作为数据存储的投票系统。1. vote.java&#xff1a;package vote;import java.io.*;import java.util.*;public class vote {public String filePath "";publi…

HTTP2指纹识别(一种相对不为人知的网络指纹识别方法)

这是关于网络指纹识别的两部分系列的第二部分上一部分我介绍了有关TLS 指纹识别方法&#xff08;以及在不同客户端的指纹有何区别&#xff09;&#xff1a;https://mp.weixin.qq.com/s/BvotXrFXwYvGWpqHKoj3uQHTTP/2 指纹识别和Tls指纹类似也是一种 Web 服务器可以依赖指纹来识…

中兴智能视觉大数据:人脸识别技术目前处于“用的不够,用的不好”

中兴智能视觉大数据报道&#xff1a;在2018年5月30日举行的“人脸识别等AI技术在校园周边安全中的应用”研讨会上&#xff0c;中国人民大学危机管理研究中心主任唐钧指出&#xff0c;人脸识别等AI技术具有“精细识别”、“提前干预”、“及时处置”等优势&#xff0c;有利于促进…

C#反射,性能优化,不止于优化

“ 架构师的价值&#xff0c;在于独立且理性的思考”想要写出灵活而且具有更好适应性的代码&#xff0c;反射是首选方案。反射赋予程序在运行时动态创建实例的能力&#xff0c;可以在程序运行时&#xff08;而非编译时&#xff09;获取实例类型&#xff0c;获取元数据信息&…